아프리카TV - 사이드바 UI 변경

아프리카TV의 사이드바 UI를 변경합니다.

当前为 2024-01-13 提交的版本,查看 最新版本

作者
askld
评分
0 0 0
版本
2024-01-12
创建于
2024-01-13
更新于
2024-01-13
大小
18.9 KB
许可证
MIT
适用于

// ==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 https://update.gf.qytechs.cn/scripts/484713/%EC%95%84%ED%94%84%EB%A6%AC%EC%B9%B4TV%20-%20%EC%82%AC%EC%9D%B4%EB%93%9C%EB%B0%94%20UI%20%EB%B3%80%EA%B2%BD.user.js
// @updateURL https://update.gf.qytechs.cn/scripts/484713/%EC%95%84%ED%94%84%EB%A6%AC%EC%B9%B4TV%20-%20%EC%82%AC%EC%9D%B4%EB%93%9C%EB%B0%94%20UI%20%EB%B3%80%EA%B2%BD.meta.js
// ==/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 = `


인기 채널


`;

// #serviceLnb 하위에 HTML 삽입
const serviceLnbElement = document.getElementById('sidebar');
if (serviceLnbElement) {
serviceLnbElement.insertAdjacentHTML('beforeend', newHtml);
}

GM_xmlhttpRequest({
method: 'GET',
url: 'https://live.afreecatv.com/api/main_broad_list_api.php?selectType=action&selectValue=myplus&orderType=view_cnt&pageNo=1&lang=ko_KR',
headers: {
'Content-Type': 'application/json',
},
onload: function(response) {
try {
// 응답을 JSON으로 파싱
const jsonResponse = JSON.parse(response.responseText);

// 응답에서 필요한 정보 추출
const channels = jsonResponse.broad;

// users-section에 동적으로 user 요소 추가
const usersSection = document.querySelector('.users-section.top');
channels.forEach(channel => {
const userElement = createUserElement(channel);
usersSection.appendChild(userElement);
});
} catch (error) {
console.error('Error parsing JSON:', error);
}
},
onerror: function(error) {
console.error('Error:', error);
}
});
}

function insertFavoriteChannels(response){

// 특정 HTML 삽입
const newHtml = `


즐겨찾기 중인 채널


`;

// #serviceLnb 하위에 HTML 삽입
const serviceLnbElement = document.getElementById('sidebar');
if (serviceLnbElement) {
serviceLnbElement.insertAdjacentHTML('beforeend', newHtml);
}

try {
// 응답에서 필요한 정보 추출
const jsonData = response;
// 데이터 배열을 순회하면서 각각의 객체에서 broad_info를 확인합니다.
jsonData.data.forEach(item => {
// broad_info가 비어있는지 확인합니다.
if (item.broad_info.length === 0) { //비방
//console.log(`broad_info is empty for user ${item.user_nick}`);
} else { //방송중
// broad_info가 비어있지 않은 경우, 여러가지 작업을 수행할 수 있습니다.
//console.log(`broad_info is not empty for user ${item.user_nick}`);
// users-section에 동적으로 user 요소 추가
const usersSection = document.querySelector('.users-section.follow');
const userElement = createUserElement(item.broad_info[0]);
usersSection.appendChild(userElement);
}
});
} catch (error) {
console.error('Error parsing JSON:', error);
}
}

function insertMyplusChannels(){

// 특정 HTML 삽입
const newHtml = `


MY+ 추천 채널


`;

// #serviceLnb 하위에 HTML 삽입
const serviceLnbElement = document.getElementById('sidebar');
if (serviceLnbElement) {
serviceLnbElement.insertAdjacentHTML('beforeend', newHtml);
}

GM_xmlhttpRequest({
method: 'GET',
url: 'https://live.afreecatv.com/api/myplus/preferbjLiveVodController.php?nInitCnt=6&szRelationType=C',
headers: {
'Content-Type': 'application/json',
},
onload: function(response) {
try {
// 응답을 JSON으로 파싱
const jsonResponse = JSON.parse(response.responseText);

// 응답에서 필요한 정보 추출
const channels = jsonResponse.DATA.live_list;

// users-section에 동적으로 user 요소 추가
const usersSection = document.querySelector('.users-section.myplus');
channels.forEach(channel => {
const userElement = createUserElement(channel);
usersSection.appendChild(userElement);
});
} catch (error) {
console.error('Error parsing JSON:', error);
}
},
onerror: function(error) {
console.error('Error:', error);
}
});
}

// GM_xmlhttpRequest를 사용하여 요청 보내기
GM_xmlhttpRequest({
method: 'GET',
url: 'https://myapi.afreecatv.com/api/favorite',
headers: {
'Content-Type': 'application/json',
},
onload: function(response) {
// 응답 수정
response = response.responseText;
response = JSON.parse(response);

// if 문으로 code 값 확인
if (response.code === -10000) {
//console.log('로그인 상태가 아닙니다.');
insertTopChannels();
} else {
//console.log('로그인 상태입니다.');
insertFavoriteChannels(response);
insertMyplusChannels();
insertTopChannels();
waitForElement('.users-section.follow > .user', function (elementSelector, element) {
// 원하는 작업 수행
desc_order('.users-section.follow');
});
waitForElement('.users-section.myplus > .user', function (elementSelector, element) {
// 원하는 작업 수행
desc_order('.users-section.myplus');
});
}


},
onerror: function(error) {
console.error('Error:', error);
}
});

// HTML 요소를 가져옵니다.
const htmlElement = document.querySelector('html');

// dark 속성의 값을 확인합니다.
const isDarkMode = htmlElement.getAttribute('dark') === 'true';

if(isDarkMode){
GM_addStyle(css_Darkmode);
} else {
GM_addStyle(css_Whitemode);
}

// .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 newButton = document.createElement('a');
newButton.href = 'https://www.afreecatv.com/?hash=all';
newButton.innerHTML = '전체';
var newButton2 = document.createElement('a');
newButton2.href = 'https://www.afreecatv.com/?hash=game';
newButton2.innerHTML = '게임';
var newButton3 = document.createElement('a');
newButton3.href = 'https://www.afreecatv.com/?hash=bora';
newButton3.innerHTML = '보.라';
var newButton4 = document.createElement('a');
newButton4.href = 'https://www.afreecatv.com/?hash=sports';
newButton4.innerHTML = '스포츠';

// .left_navbar에 버튼 삽입
leftNavbar.appendChild(newButton4);
leftNavbar.appendChild(newButton3);
leftNavbar.appendChild(newButton2);
leftNavbar.appendChild(newButton);

waitForElement('.left_nav_button', function (elementSelector, element) {
// 원하는 작업 수행
// Get the current page URL
const currentPage = window.location.href;

// Get all navigation links
const navLinks = document.querySelectorAll('.left_nav_button');

// Loop through each link and check if it matches the current page
navLinks.forEach(link => {
var parentLink = link.parentElement;
if (parentLink.href === currentPage) {
link.classList.add('active'); // Add the 'active' class if it matches
}
});
});
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址