// ==UserScript== // @name 아프리카TV - 사이드바 UI 변경 // @name:ko 아프리카TV - 사이드바 UI 변경 // @namespace https://www.afreecatv.com/ // @version 2024-01-14 // @description 아프리카TV의 사이드바 UI를 변경합니다. // @description:ko 아프리카TV의 사이드바 UI를 변경합니다. // @author You // @match https://afreecatv.com/ // @match https://afreecatv.com/?hash=* // @match https://www.afreecatv.com/ // @match https://www.afreecatv.com/?hash=* // @icon https://www.google.com/s2/favicons?sz=64&domain=afreecatv.com // @grant GM_addStyle // @grant GM_xmlhttpRequest // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; const css_Darkmode = ` .left_navbar { display: flex; align-items: center; justify-content: flex-end; position: absolute; flex-direction: row-reverse; top: 0px; left: 160px; /* 변경된 부분: left 속성으로 수정 */ } .left_nav_button { font-family: Arial, Helvetica, sans-serif; /* 나눔고딕 대신 sans-serif 폰트 중 하나를 선택하여 적용 */ 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:150px; } #sidebar .top-section { display: flex; align-items: center; justify-content: space-around; margin: 10px 0px; } #sidebar .top-section>span { text-transform: uppercase; font-weight: 550; font-size: 14px; margin-top: 6px; margin-bottom: 4px; } #sidebar .twitch-message-section { margin: 0px 10px; margin-top: 10px; padding: 25px; border-radius: 8px; background-color: #18181b; box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.9); } #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; } .user { display: grid; grid-template-areas: "profile-picture username watchers" "profile-picture description blank"; grid-template-columns: 40px auto auto; padding: 6px 10px; } .user:hover { background-color: #26262c; cursor: pointer; } .user .profile-picture { grid-area: profile-picture; width: 32px; height: 32px; border-radius: 50%; } .user .username { grid-area: username; /*font-size: 0.9rem;*/ font-size: 15px; font-weight: 550; } .user .description { grid-area: description; /*font-size: 0.8rem;*/ font-size: 13px; color: #a1a1a1; /* font-weight: 500; */ letter-spacing: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .user .watchers { grid-area: watchers; display: flex; align-items: center; justify-content: flex-end; /*font-size: 0.9rem;*/ font-size: 13px; color: #c0c0c0; margin-right: 2px; } .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 { position: relative; display: inline-block; border-bottom: 1px dotted black; } .tooltip .tooltiptext { visibility: hidden; width: 120px; background-color: black; color: #fff; text-align: center; border-radius: 6px; padding: 5px 0; /* Position the tooltip */ position: absolute; z-index: 1; top: -5px; left: 105%; } .tooltip:hover .tooltiptext { visibility: visible; } .tooltip-container { position: relative; } .tooltip { position: absolute; z-index: 999; width: 240px; background-color: black; color: #fff; text-align: center; border-radius: 6px; padding: 5px 0; visibility: hidden; top: 45px; left: 0; } .tooltip-container:hover .tooltip { visibility: visible; } .tooltip-container .tooltip:hover { visibility: hidden; } `; const css_Whitemode = ` .left_navbar { display: flex; align-items: center; justify-content: flex-end; position: absolute; flex-direction: row-reverse; top: 0px; left: 160px; /* 변경된 부분: left 속성으로 수정 */ } .left_nav_button { font-family: Arial, Helvetica, sans-serif; /* 나눔고딕 대신 sans-serif 폰트 중 하나를 선택하여 적용 */ position: relative; width: 70px; height: 70px; padding: 0; border: 0; border-radius: 50%; cursor: pointer; z-index: 3001; transition: all .2s; color: black; 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:150px; } #sidebar .top-section { display: flex; align-items: center; justify-content: space-around; margin: 10px 0px; } #sidebar .top-section>span { text-transform: uppercase; font-weight: 600; font-size: 14px; margin-top: 6px; margin-bottom: 4px; } #sidebar .twitch-message-section { margin: 0px 10px; margin-top: 10px; padding: 25px; border-radius: 8px; background-color: #18181b; box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.9); } #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; } .user { display: grid; grid-template-areas: "profile-picture username watchers" "profile-picture description blank"; grid-template-columns: 40px auto auto; padding: 6px 10px; } .user:hover { background-color: #E6E6EA; cursor: pointer; } .user .profile-picture { grid-area: profile-picture; width: 32px; height: 32px; border-radius: 50%; } .user .username { grid-area: username; font-size: 15px; font-weight: 600; } .user .description { grid-area: description; font-size: 13px; color: #53535F; letter-spacing: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .user .watchers { grid-area: watchers; display: flex; align-items: center; justify-content: flex-end; font-size: 13px; color: black; margin-right: 2px; } .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 { position: relative; } .tooltip { position: absolute; z-index: 999; width: 240px; background-color: black; color: #fff; text-align: center; border-radius: 6px; padding: 5px 0; visibility: hidden; top: 45px; left: 0; } .tooltip-container:hover .tooltip { visibility: visible; } .tooltip-container .tooltip:hover { visibility: hidden; } `; function waitForElement(elementSelector, callBack) { const element = document.querySelector(elementSelector); if (element) { callBack(elementSelector, element); } else { setTimeout(function () { waitForElement(elementSelector, callBack); }, 1000); } } function desc_order(selector){ // Get the container element const container = document.querySelector(selector); // Get all user elements const userElements = document.querySelectorAll(`${selector} >.user.tooltip-container`); // 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 addNumberSeparator(number) { // toLocaleString 메서드를 사용하여 숫자에 구분자 추가 number = Number(number); return number.toLocaleString(); } // 사용자 요소를 생성하는 함수 function createUserElement(channel) { const userElement = document.createElement('div'); const playerLink = "https://play.afreecatv.com/"+channel.user_id; userElement.classList.add('user'); userElement.classList.add('tooltip-container'); userElement.setAttribute('onclick',`window.open('${playerLink}', '_blank')`); userElement.setAttribute('data-watchers',`${channel.total_view_cnt}`); const tooltip = document.createElement('div'); tooltip.classList.add('tooltip'); tooltip.textContent = channel.broad_title; const profilePicture = document.createElement('img'); const pp_webp="https://stimg.afreecatv.com/LOGO/"+channel.user_id.slice(0, 2)+"/"+channel.user_id+"/m/"+channel.user_id+".webp"; const pp_jpg="https://profile.img.afreecatv.com/LOGO/"+channel.user_id.slice(0, 2)+"/"+channel.user_id+"/m/"+channel.user_id+".jpg"; profilePicture.src = pp_webp; // 프로필사진 profilePicture.setAttribute('onerror', `this.onerror=null; this.src='${pp_jpg}'`); profilePicture.setAttribute('alt', `${channel.user_id}'`); //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 = channel.user_nick; //스트리머명 const cat_no = channel.broad_cate_no; const categoryList = oMainCategory.category_list; const filteredList = categoryList.filter(word => !["전체", "제한"].some(keyword => word.menu_name.includes(keyword))); const targetActionContent = cat_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 : cat_no; if(result==="00040121"){ result = "종합게임"; } const description = document.createElement('span'); description.classList.add('description'); description.textContent = result; //카테고리 const watchers = document.createElement('span'); watchers.classList.add('watchers'); watchers.innerHTML = `🔴${addNumberSeparator(channel.total_view_cnt)}`; //시청자수 userElement.appendChild(tooltip); userElement.appendChild(profilePicture); userElement.appendChild(username); userElement.appendChild(description); userElement.appendChild(watchers); return userElement; } // 특정 HTML 삽입 const newHtml = `
`; // #serviceLnb 하위에 HTML 삽입 const serviceLnbElement = document.getElementById('serviceLnb'); if (serviceLnbElement) { serviceLnbElement.insertAdjacentHTML('beforeend', newHtml); } function insertTopChannels(){ // 특정 HTML 삽입 const newHtml = `