├── Better_Multichzzk ├── Better_Multichzzk.user.js └── README.md ├── CHZZK_Always_Awake ├── CHZZK_Always_Awake.user.js └── README.md ├── CHZZK_Better_Shorts_Player ├── CHZZK_Better_Shorts_Player.user.js └── README.md ├── CHZZK_Favorite_Streamer ├── CHZZK_Favorite_Streamer.user.js └── README.md ├── CHZZK_Hide_Blind_Chat_VOD ├── CHZZK_Hide_Blind_Chat_VOD.user.js └── README.md ├── CHZZK_Live_Progress_Slider ├── CHZZK_Live_Progress_Slider.user.js ├── README.md └── preview.png ├── CHZZK_Max_Quality ├── CHZZK_Max_Quality.user.js └── README.md ├── CHZZK_Never_Stop_At_Start ├── CHZZK_Never_Stop_At_Start.user.js └── README.md ├── CHZZK_Restore_Blind_Chat ├── CHZZK_Restore_Blind_Chat.user.js └── README.md ├── CHZZK_User_Memo ├── CHZZK_User_Memo.user.js ├── README.md └── images │ ├── all_memo.png │ ├── button.png │ ├── changenick.png │ ├── copy.png │ ├── new_memo.png │ ├── setting.png │ └── usermenu.png ├── CHZZK_Video_RealTime ├── CHZZK_Video_RealTime.user.js ├── README.md └── images │ └── preview.png ├── CHZZK_sign_in_iframe ├── CHZZK_sign_in_iframe.user.js └── README.md ├── LICENSE └── README.md /Better_Multichzzk/Better_Multichzzk.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Better_Multichzzk 3 | // @namespace Better_Multichzzk 4 | // @version 0.0.5 5 | // @description Better player for multichzzk 6 | // @author Nomo 7 | // @match https://multichzzk.tv/* 8 | // @match https://mul.live/* 9 | // @match https://chzzk.naver.com/*?multichzzk 10 | // @supportURL https://github.com/nomomo/Chzzk_Scripts/issues 11 | // @homepage https://github.com/nomomo/Chzzk_Scripts/ 12 | // @downloadURL https://github.com/nomomo/Chzzk_Scripts/raw/main/Better_Multichzzk/Better_Multichzzk.user.js 13 | // @updateURL https://github.com/nomomo/Chzzk_Scripts/raw/main/Better_Multichzzk/Better_Multichzzk.user.js 14 | // ==/UserScript== 15 | 16 | (function () { 17 | 'use strict'; 18 | let debug = false; 19 | 20 | let url = document.location.href; 21 | if (url.indexOf("//multichzzk.tv/") !== -1 || url.indexOf("//mul.live/") !== -1) { 22 | let iframes = document.querySelectorAll('iframe'); 23 | if (debug) console.log("iframes", iframes); 24 | 25 | iframes.forEach(function (iframe) { 26 | if (iframe.src.indexOf('//chzzk.naver.com/') !== -1 && iframe.src.indexOf('?multichzzk') === -1) { 27 | iframe.src = iframe.src + '?multichzzk'; 28 | } 29 | }); 30 | } else if (url.indexOf("//chzzk.naver.com/") !== -1) { 31 | let isTopWindow = window.self === window.top; 32 | if (isTopWindow) { 33 | return; 34 | } 35 | 36 | if (debug) console.log("chzzk embed", url); 37 | 38 | function waitForElement(selector, callback) { 39 | const existingElement = document.querySelector(selector); 40 | 41 | if (existingElement) { 42 | callback(existingElement); 43 | } else { 44 | const observer = new MutationObserver((mutationsList) => { 45 | const targetElement = document.querySelector(selector); 46 | if (targetElement) { 47 | observer.disconnect(); 48 | callback(targetElement); 49 | } 50 | }); 51 | 52 | observer.observe(document.documentElement, { 53 | childList: true, 54 | subtree: true, 55 | }); 56 | } 57 | } 58 | 59 | let player = undefined; 60 | let handleVideoReadyFired = false; 61 | 62 | let handleVideoReady = function(){ 63 | if (handleVideoReadyFired) return; 64 | handleVideoReadyFired = true; 65 | let viewmode_buttons = document.querySelectorAll(".pzp-pc__viewmode-button"); 66 | if(viewmode_buttons.length == 1){ 67 | viewmode_buttons[0].click(); 68 | } 69 | else{ 70 | // 치즈나이프와의 충돌 방지 71 | for (let i = 0; i < viewmode_buttons.length; i++) { 72 | let button = viewmode_buttons[i]; 73 | if (button.getAttribute('aria-label') === '넓은 화면') { 74 | button.click(); 75 | break; 76 | } 77 | } 78 | } 79 | document.querySelector('[class^="live_chatting_header_button__"]').click(); 80 | }; 81 | 82 | waitForElement("video.webplayer-internal-video", function (node) { 83 | if (debug) console.log("found video player", node); 84 | player = node; 85 | if (player.readyState >= 2) { 86 | handleVideoReady(); 87 | } else { 88 | player.addEventListener('loadedmetadata', function once() { 89 | player.removeEventListener('loadedmetadata', once); 90 | handleVideoReady(); 91 | }); 92 | } 93 | player.muted = true; 94 | }); 95 | 96 | } 97 | 98 | })(); -------------------------------------------------------------------------------- /Better_Multichzzk/README.md: -------------------------------------------------------------------------------- 1 | # Better_Multichzzk 2 | 3 | mul.live에서 자동으로 아래의 것들을 해줍니다. 4 | 5 | - 자동으로 화면 넓게 6 | - 채팅창 숨기기 7 | - 모두 음소거 8 | 9 | ## Install 10 | 11 | 설치 방법을 설명합니다. 12 | 13 | ### STEP 1. ScriptManager 14 | 15 | 아래 리스트에서 본인이 사용 중인 브라우저에 맞는 링크에 접속한 후, 유저스크립트 관리 확장기능인 Tampermonkey 를 설치하세요. 16 | 17 | - Chrome - [Tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo) 18 | - Firefox - [Tampermonkey](https://addons.mozilla.org/ko/firefox/addon/tampermonkey/) 19 | - Opera - [Tampermonkey](https://addons.opera.com/extensions/details/tampermonkey-beta/) 20 | - Safari - [Tampermonkey](https://safari.tampermonkey.net/tampermonkey.safariextz) 21 | - Edge - [Tampermonkey](https://microsoftedge.microsoft.com/addons/detail/tampermonkey/iikmkjmpaadaobahmlepeloendndfphd) 22 | 23 | ### STEP 2. UserScript 24 | 25 | - 유저스크립트 관리 확장기능 설치 후 아래의 링크를 클릭하세요. 이후 뜨는 창에서 "설치" 버튼을 눌러 스크립트를 설치합니다. 26 | - [https://github.com/nomomo/Chzzk_Scripts/raw/main/Better_Multichzzk/Better_Multichzzk.user.js](https://github.com/nomomo/Chzzk_Scripts/raw/main/Better_Multichzzk/Better_Multichzzk.user.js) 27 | 28 | 이것으로 설치는 끝입니다. 즐겁게 사용하세요~ 29 | 30 | > 주의: 본 스크립트를 설치 및 사용하며 브라우저 과부하로 인한 응답 없음/뻗음으로 인한 데이터 손실이나 기타 발생하는 다른 문제에 대하여 개발자는 책임지지 않음(보고된 문제는 없음) 31 | > 본 스크립트는 Tampermonkey 외의 스크립트 매니저에서는 정상 동작하지 않을 수 있습니다. 32 | 33 | ### 0.0.5 - Feb. 18, 2024 34 | 35 | - mul.live 도메인 추가 36 | 37 | ### 0.0.4 - Dec. 23, 2023 38 | 39 | - 음소거 기능 누락 문제 수정 40 | 41 | ### 0.0.3 - Dec. 23, 2023 42 | 43 | - 치지직과 트위치를 같이 멀티스트림으로 시청하는 경우 트위치 재생이 올바르게 되지 않는 문제 수정 44 | 45 | ### 0.0.2 - Dec. 23, 2023 46 | 47 | - 치즈나이프 확장 프로그램과 충돌하여 치지직 플레이어가 자동으로 PIP 모드가 되는 버그 수정 48 | 49 | ### 0.0.1 - Dec. 23, 2023 50 | 51 | - 최초 커밋 52 | 53 | ## License 54 | 55 | MIT 56 | 57 | ## Happy?? 58 | 59 | Buy Me A Coffee -------------------------------------------------------------------------------- /CHZZK_Always_Awake/CHZZK_Always_Awake.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name CHZZK_Always_Awake 3 | // @namespace CHZZK_Always_Awake 4 | // @version 0.0.1 5 | // @description Pretends to be active even when the tab is inactive. 6 | // @author Nomo 7 | // @match https://chzzk.naver.com/* 8 | // @supportURL https://github.com/nomomo/Chzzk_Scripts/issues 9 | // @homepage https://github.com/nomomo/Chzzk_Scripts/ 10 | // @downloadURL https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_Always_Awake/CHZZK_Always_Awake.user.js 11 | // @updateURL https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_Always_Awake/CHZZK_Always_Awake.user.js 12 | // @grant unsafeWindow 13 | // @run-at document-start 14 | // ==/UserScript== 15 | 16 | (async () => { 17 | 'use strict'; 18 | var DEBUG = false; 19 | var NOMO_DEBUG = function ( /**/ ) { 20 | if (!DEBUG) return; 21 | var args = arguments, args_length = args.length, args_copy = args; 22 | for (let i = args_length; i > 0; i--) args[i] = args_copy[i - 1]; 23 | args[0] = "[CAA] "; 24 | args.length = args_length + 1; 25 | console.log.apply(console, args); 26 | }; 27 | var date_n = Number(new Date()); 28 | 29 | NOMO_DEBUG("GM_SETTINGS.disable_visibilitychange: true"); 30 | try{ 31 | Object.defineProperty(document, 'hidden', { 32 | value: false, 33 | writable: false 34 | }); 35 | } 36 | catch(e){ 37 | NOMO_DEBUG("disable_visibilitychange error - hidden redefine", e); 38 | } 39 | 40 | try{ 41 | Object.defineProperty(document, 'visibilityState', { 42 | value: 'visible', 43 | writable: false 44 | }); 45 | } 46 | catch(e){ 47 | NOMO_DEBUG("disable_visibilitychange error - visibilityState redefine", e); 48 | } 49 | 50 | try{ 51 | Object.defineProperty(document, 'webkitVisibilityState', { 52 | value: 'visible', 53 | writable: false 54 | }); 55 | } 56 | catch(e){ 57 | NOMO_DEBUG("disable_visibilitychange error - webkitVisibilityState redefine", e); 58 | } 59 | 60 | try{ 61 | document.dispatchEvent(new Event('visibilitychange')); 62 | } 63 | catch(e){ 64 | NOMO_DEBUG("disable_visibilitychange error - visibilitychange dispatchEvent", e); 65 | } 66 | 67 | try{ 68 | document.hasFocus = function () { 69 | return true; 70 | }; 71 | } 72 | catch(e){ 73 | NOMO_DEBUG("disable_visibilitychange error - hasFocus return true", e); 74 | } 75 | 76 | try{ 77 | unsafeWindow["_addEventListener_" + date_n] = unsafeWindow.addEventListener; 78 | unsafeWindow.addEventListener = function (a, b, c) { 79 | if (a === "visibilitychange" || a === "blur" || a === "webkitvisibilitychange") { 80 | return; 81 | } 82 | 83 | if (c == undefined){ 84 | c = false; 85 | } 86 | unsafeWindow["_addEventListener_" + date_n](a, b, c); 87 | }; 88 | } 89 | catch(e){ 90 | NOMO_DEBUG("disable_visibilitychange error - overwrite window addEventListener", e); 91 | } 92 | 93 | try{ 94 | unsafeWindow.document["_addEventListener_" + date_n] = unsafeWindow.document.addEventListener; 95 | unsafeWindow.document.addEventListener = function (a, b, c) { 96 | if (a === "visibilitychange" || a === "blur" || a === "webkitvisibilitychange") { 97 | return; 98 | } 99 | 100 | if (c == undefined){ 101 | c = false; 102 | } 103 | unsafeWindow.document["_addEventListener_" + date_n](a, b, c); 104 | }; 105 | } 106 | catch(e){ 107 | NOMO_DEBUG("disable_visibilitychange error - overwrite document addEventListener", e); 108 | } 109 | 110 | })(); 111 | -------------------------------------------------------------------------------- /CHZZK_Always_Awake/README.md: -------------------------------------------------------------------------------- 1 | # CHZZK Always Awake 2 | 3 | - 탭이 비활성 되어도 비활성이 아닌 것처럼 동작하도록 합니다. 4 | 5 | ## Install 6 | 7 | 설치 방법을 설명합니다. 8 | 9 | ### STEP 1. ScriptManager 10 | 11 | 아래 리스트에서 본인이 사용 중인 브라우저에 맞는 링크에 접속한 후, 유저스크립트 관리 확장기능인 Tampermonkey 를 설치하세요. 12 | 13 | - Chrome - [Tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo) 14 | - Firefox - [Tampermonkey](https://addons.mozilla.org/ko/firefox/addon/tampermonkey/) 15 | - Opera - [Tampermonkey](https://addons.opera.com/extensions/details/tampermonkey-beta/) 16 | - Safari - [Tampermonkey](https://safari.tampermonkey.net/tampermonkey.safariextz) 17 | - Edge - [Tampermonkey](https://microsoftedge.microsoft.com/addons/detail/tampermonkey/iikmkjmpaadaobahmlepeloendndfphd) 18 | 19 | ### STEP 2. UserScript 20 | 21 | - 유저스크립트 관리 확장기능 설치 후 아래의 링크를 클릭하세요. 이후 뜨는 창에서 "설치" 버튼을 눌러 스크립트를 설치합니다. 22 | - [https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_Always_Awake/CHZZK_Always_Awake.user.js](https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_Always_Awake/CHZZK_Always_Awake.user.js) 23 | 24 | 이것으로 설치는 끝입니다. 즐겁게 사용하세요~ 25 | 26 | > 주의: 본 스크립트를 설치 및 사용하며 브라우저 과부하로 인한 응답 없음/뻗음으로 인한 데이터 손실이나 기타 발생하는 다른 문제에 대하여 개발자는 책임지지 않음(보고된 문제는 없음) 27 | > 본 스크립트는 Tampermonkey 외의 스크립트 매니저에서는 정상 동작하지 않을 수 있습니다. 28 | 29 | ### 0.0.1 - Apr. 11, 2024 30 | 31 | - 최초 커밋 32 | 33 | ## License 34 | 35 | MIT 36 | 37 | ## Happy?? 38 | 39 | Buy Me A Coffee -------------------------------------------------------------------------------- /CHZZK_Better_Shorts_Player/CHZZK_Better_Shorts_Player.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name CHZZK Better Shorts Player 3 | // @namespace CHZZK_Better_Shorts_Player 4 | // @version 0.0.1 5 | // @description Chzzk 의 클립 보기 화면 개선 6 | // @author Nomo 7 | // @match https://m.naver.com/shorts/* 8 | // @homepageURL https://github.com/nomomo/Chzzk_Scripts/ 9 | // @downloadURL https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_Better_Shorts_Player/CHZZK_Better_Shorts_Player.user.js 10 | // @updateURL https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_Better_Shorts_Player/CHZZK_Better_Shorts_Player.user.js 11 | // @require https://cdn.jsdelivr.net/npm/arrive@2.4.1/minified/arrive.min.js 12 | // @grant GM_getValue 13 | // @grant GM_setValue 14 | // @grant GM_addStyle 15 | // ==/UserScript== 16 | 17 | (function() { 18 | 'use strict'; 19 | 20 | GM_addStyle(` 21 | /* 기본적으로 숨기기 */ 22 | #app .sp_progress_wrap, 23 | #app .section_header, 24 | #app .section_info { 25 | opacity: 0; 26 | transition: opacity 0.3s ease-in-out; 27 | } 28 | 29 | /* #app에 마우스가 올라가 있을 때만 표시 */ 30 | #app:hover .sp_progress_wrap, 31 | #app:hover .section_header, 32 | #app:hover .section_info { 33 | opacity: 1; 34 | } 35 | 36 | `); 37 | 38 | const volumeControlClass = 'custom-volume-control-wrapper'; 39 | const handleDefaultSize = 10; // 기본 핸들 크기 40 | const handleHoverSize = 14; // 마우스 오버 시 핸들 크기 41 | let isDragging = false; // 드래그 상태 플래그 42 | 43 | // 기존 볼륨 컨트롤러 제거 함수 44 | function removeExistingVolumeControl() { 45 | document.querySelector(`.${volumeControlClass}`)?.remove(); 46 | } 47 | 48 | // 스타일 설정 함수 49 | function setStyles(element, styles) { 50 | Object.assign(element.style, styles); 51 | } 52 | 53 | // 핸들 크기 설정 함수 54 | function setHandleSize(handle, size, animate = true) { 55 | if (animate) { 56 | handle.style.transition = 'width 0.2s ease, height 0.2s ease, left 0.2s ease'; 57 | } else { 58 | handle.style.transition = 'none'; 59 | } 60 | handle.style.width = `${size}px`; 61 | handle.style.height = `${size}px`; 62 | handle.style.left = `${-size / 2 + 1}px`; // 중앙 정렬을 위한 위치 조정 63 | } 64 | 65 | // 볼륨 컨트롤러 추가 함수 66 | function addVolumeControl() { 67 | console.log("addVolumeControl"); 68 | // 기존 컨트롤러 제거 69 | removeExistingVolumeControl(); 70 | 71 | // video 엘리먼트 찾기 72 | const contentWrap = document.querySelector('.sh_wrap'); 73 | if (!contentWrap) return; 74 | 75 | // 이전에 저장된 볼륨 값 불러오기 (없으면 기본값 1.0) 76 | const savedVolume = GM_getValue('videoVolume', 1.0); 77 | 78 | // 볼륨 컨트롤러 래퍼 엘리먼트 생성 79 | const volumeWrapper = document.createElement('div'); 80 | volumeWrapper.className = volumeControlClass; 81 | setStyles(volumeWrapper, { 82 | position: 'absolute', 83 | right: '12px', 84 | top: '40px', 85 | width: '30px', 86 | height: '120px', 87 | zIndex: '9999', 88 | display: 'flex', 89 | alignItems: 'center', 90 | justifyContent: 'center', 91 | flexDirection: 'column', 92 | pointerEvents: 'auto' 93 | }); 94 | 95 | // 볼륨 컨트롤러 엘리먼트 생성 96 | const volumeControl = document.createElement('div'); 97 | volumeControl.className = 'custom-volume-control'; 98 | setStyles(volumeControl, { 99 | width: '2px', // 컨트롤 바 너비 2px 100 | height: `calc(100% - ${handleHoverSize * 2}px)`, // 핸들 크기만큼 공간 확보 101 | backgroundColor: '#333', // 기본 어두운 회색 102 | borderRadius: '5px', 103 | position: 'relative', 104 | cursor: 'pointer', 105 | marginTop: `${handleHoverSize}px`, // 핸들이 벗어나지 않도록 상단에 마진 추가 106 | }); 107 | 108 | // 볼륨 조절 원 생성 109 | const volumeHandle = document.createElement('div'); 110 | setHandleSize(volumeHandle, handleDefaultSize, false); // 기본 크기로 설정, 애니메이션 없음 111 | setStyles(volumeHandle, { 112 | backgroundColor: '#FFFFFF', 113 | borderRadius: '50%', 114 | position: 'absolute', 115 | bottom: '0', 116 | cursor: 'pointer', 117 | }); 118 | 119 | // 핸들에 마우스 오버 시 핸들 크기 확대 120 | volumeHandle.addEventListener('mouseover', () => { 121 | if (!isDragging) { 122 | setHandleSize(volumeHandle, handleHoverSize); 123 | } 124 | }); 125 | 126 | volumeHandle.addEventListener('mouseout', () => { 127 | if (!isDragging) { 128 | setHandleSize(volumeHandle, handleDefaultSize); 129 | } 130 | }); 131 | 132 | // 비디오 엘리먼트 가져오기 133 | const video = document.querySelector('.webplayer-internal-video'); 134 | if (video) { 135 | // 이전에 저장된 볼륨 값 적용 136 | video.volume = savedVolume; 137 | // 현재 비디오 볼륨에 따라 볼륨 핸들 위치 설정 및 컨트롤 바 색상 업데이트 138 | volumeHandle.style.bottom = `calc(${video.volume * 100}% - ${handleHoverSize / 2}px)`; 139 | volumeControl.style.background = `linear-gradient(to top, #fff ${video.volume * 100}%, #333 ${video.volume * 100}%)`; 140 | } 141 | 142 | // 볼륨 조절 함수 143 | function setVolume(y) { 144 | const rect = volumeControl.getBoundingClientRect(); 145 | let volumeLevel = (rect.bottom - y) / rect.height; 146 | volumeLevel = Math.min(Math.max(volumeLevel, 0), 1); // 0 ~ 1 사이로 제한 147 | 148 | if (video) { 149 | video.volume = volumeLevel; 150 | GM_setValue('videoVolume', volumeLevel); // 볼륨 값 저장 151 | } 152 | 153 | // 볼륨 핸들 위치 조정 154 | volumeHandle.style.bottom = `calc(${volumeLevel * 100}% - ${handleHoverSize / 2}px)`; 155 | // 볼륨에 따라 바 색상 업데이트 156 | volumeControl.style.background = `linear-gradient(to top, #fff ${volumeLevel * 100}%, #333 ${volumeLevel * 100}%)`; 157 | } 158 | 159 | // 클릭 및 드래그 이벤트 핸들러 160 | function onMouseMove(e) { 161 | setVolume(e.clientY); 162 | } 163 | 164 | volumeControl.addEventListener('mousedown', function(e) { 165 | isDragging = true; 166 | setHandleSize(volumeHandle, handleHoverSize); // 드래그 시작 시 핸들 크기 확대 167 | setVolume(e.clientY); // 처음 클릭 시 볼륨 설정 168 | document.addEventListener('mousemove', onMouseMove); 169 | document.addEventListener('mouseup', function() { 170 | document.removeEventListener('mousemove', onMouseMove); 171 | isDragging = false; 172 | setHandleSize(volumeHandle, handleDefaultSize); // 드래그 종료 시 핸들 크기 원상복귀 173 | }, { once: true }); 174 | }); 175 | 176 | // 볼륨 컨트롤러에 핸들 추가 177 | volumeControl.appendChild(volumeHandle); 178 | volumeWrapper.appendChild(volumeControl); 179 | contentWrap.appendChild(volumeWrapper); 180 | } 181 | 182 | // .video 엘리먼트가 생성되었을 때 볼륨 컨트롤러 추가 183 | document.arrive('.webplayer-internal-video', {existing: true}, addVolumeControl); 184 | 185 | })(); 186 | -------------------------------------------------------------------------------- /CHZZK_Better_Shorts_Player/README.md: -------------------------------------------------------------------------------- 1 | # CHZZK Better Shorts Player 2 | 3 | > ⚠️ **Deprecated** 4 | > 이 스크립트는 더 이상 유지보수되지 않으며, 설치를 권장하지 않습니다. 5 | > 클립 페이지에 볼륨 컨트롤러 기능이 **네이티브로 추가**되어 더 이상 필요하지 않게 되었습니다. 6 | 7 | - Chzzk의 클립 페이지에 볼륨 컨트롤러를 추가해줘요. 8 | - 이전에 설정한 볼륨을 기억해요. 9 | - 플레이어에 마우스를 올린 상태에서만 오버레이(제목, 재생바, 컨트롤 버튼 등)를 표시해요. 10 | 11 | ## Preview 12 | 13 | https://github.com/user-attachments/assets/83aabceb-e2e9-4cc4-bc9a-91d3568e82e9 14 | 15 | ## Install 16 | 17 | 설치 방법을 설명합니다. 18 | 19 | ### STEP 1. ScriptManager 20 | 21 | 아래 리스트에서 본인이 사용 중인 브라우저에 맞는 링크에 접속한 후, 유저스크립트 관리 확장기능인 Tampermonkey 를 설치하세요. 22 | 23 | - Chrome - [Tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo) 24 | - Firefox - [Tampermonkey](https://addons.mozilla.org/ko/firefox/addon/tampermonkey/) 25 | - Opera - [Tampermonkey](https://addons.opera.com/extensions/details/tampermonkey-beta/) 26 | - Safari - [Tampermonkey](https://safari.tampermonkey.net/tampermonkey.safariextz) 27 | - Edge - [Tampermonkey](https://microsoftedge.microsoft.com/addons/detail/tampermonkey/iikmkjmpaadaobahmlepeloendndfphd) 28 | 29 | ### STEP 2. UserScript 30 | 31 | - 유저스크립트 관리 확장기능 설치 후 아래의 링크를 클릭하세요. 이후 뜨는 창에서 "설치" 버튼을 눌러 스크립트를 설치합니다. 32 | - [https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_Better_Shorts_Player/CHZZK_Better_Shorts_Player.user.js](https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_Better_Shorts_Player/CHZZK_Better_Shorts_Player.user.js) 33 | 34 | 이것으로 설치는 끝입니다. 즐겁게 사용하세요~ 35 | 36 | > 주의: 본 스크립트를 설치 및 사용하며 브라우저 과부하로 인한 응답 없음/뻗음으로 인한 데이터 손실이나 기타 발생하는 다른 문제에 대하여 개발자는 책임지지 않음(보고된 문제는 없음) 37 | > 본 스크립트는 Tampermonkey 외의 스크립트 매니저에서는 정상 동작하지 않을 수 있습니다. 38 | 39 | ## Note 40 | 41 | - 코드의 95% 이상을 ChatGPT로 작성했어요. 예외처리 같은 것이 제대로 안 되어 있긴 한데 현재는 대충 잘 되는 것 같아요. 문제가 생기면 스크립트를 비활성화 하세요. 42 | 43 | ### 0.0.1 - Aug. 16, 2024 44 | 45 | - 최초 커밋 46 | 47 | ## License 48 | 49 | MIT 50 | 51 | ## Happy?? 52 | 53 | Buy Me A Coffee 54 | -------------------------------------------------------------------------------- /CHZZK_Favorite_Streamer/CHZZK_Favorite_Streamer.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name CHZZK Favorite Streamer 3 | // @namespace CHZZK_Favorite_Streamer 4 | // @version 0.0.3 5 | // @description 즐겨찾는 스트리머를 목록 상단에 표시하는 스크립트 6 | // @author Nomo 7 | // @match https://chzzk.naver.com/* 8 | // @homepageURL https://github.com/nomomo/Chzzk_Scripts/CHZZK_Favorite_Streamers/ 9 | // @downloadURL https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_Favorite_Streamer/CHZZK_Favorite_Streamer.user.js 10 | // @updateURL https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_Favorite_Streamer/CHZZK_Favorite_Streamer.user.js 11 | // @run-at document-start 12 | // @grant unsafeWindow 13 | // @grant GM.getValue 14 | // @grant GM.setValue 15 | // @grant GM.addStyle 16 | // @grant GM.info 17 | // @require https://code.jquery.com/jquery-3.7.1.min.js#sha256=fc9a93dd241f6b045cbff0481cf4e1901becd0e12fb45166a8f17f95823f0b1a 18 | // @require https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.min.js#sha256=WXHeZwrvHW+Qpj5u2NCVyiL5XEVf/AzrYL5i4w4aRHM= 19 | // @require https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.2/Sortable.min.js#sha256=ymhDBwPE9ZYOkHNYZ8bpTSm1o943EH2BAOWjAQB+nm4= 20 | // ==/UserScript== 21 | 22 | (async function() { 23 | 'use strict'; 24 | 25 | let initfavoriteStreamers = []; 26 | 27 | // favoriteStreamers를 불러오기 28 | let favoriteStreamers = await GM.getValue('favoriteStreamers', initfavoriteStreamers); 29 | 30 | // 전역 변수에 JSON 응답 데이터 저장 31 | let jsonResponse = null; 32 | 33 | // JSON 응답 데이터를 저장하는 함수 34 | function saveResponseData(response) { 35 | jsonResponse = JSON.parse(JSON.stringify(response)); 36 | } 37 | 38 | // 원본 XMLHttpRequest를 저장 39 | const originalXHR = unsafeWindow.XMLHttpRequest; 40 | 41 | let followingChannelCreated = false; 42 | 43 | // 커스텀 XMLHttpRequest를 생성 44 | function CustomXHR() { 45 | const xhr = new originalXHR(); 46 | const originalOpen = xhr.open; 47 | const originalSend = xhr.send; 48 | let intercepted = false; 49 | let requestUrl = ""; 50 | 51 | xhr.open = function(method, url, async, user, password) { 52 | requestUrl = url; 53 | if (url.includes('https://api.chzzk.naver.com/service/v1/channels/following-lives') || url.includes('https://api.chzzk.naver.com/service/v1/channels/followings/live')) { 54 | intercepted = true; 55 | } 56 | originalOpen.apply(xhr, arguments); 57 | }; 58 | 59 | xhr.send = function(body) { 60 | if (intercepted) { 61 | xhr.addEventListener('readystatechange', async function() { 62 | if (xhr.readyState === 4 && xhr.status === 200) { 63 | let responseData = JSON.parse(xhr.responseText); 64 | saveResponseData(responseData); 65 | 66 | if (responseData.code === 200 && responseData.content && responseData.content.followingList) { 67 | favoriteStreamers = await GM.getValue('favoriteStreamers', initfavoriteStreamers); 68 | 69 | // 설정된 채널을 우선순위로 정렬 70 | responseData.content.followingList.sort((a, b) => { 71 | const aIndex = favoriteStreamers.findIndex(fav => fav.channelId === a.channelId); 72 | const bIndex = favoriteStreamers.findIndex(fav => fav.channelId === b.channelId); 73 | 74 | if (aIndex === -1 && bIndex === -1) { 75 | return 0; // 둘 다 설정된 채널이 아닌 경우 원래 순서 유지 76 | } else if (aIndex === -1) { 77 | return 1; // a가 설정된 채널이 아닌 경우 b가 우선 78 | } else if (bIndex === -1) { 79 | return -1; // b가 설정된 채널이 아닌 경우 a가 우선 80 | } else { 81 | return aIndex - bIndex; // 둘 다 설정된 채널인 경우 설정된 순서대로 정렬 82 | } 83 | }); 84 | 85 | // 원본 responseText를 수정된 데이터로 덮어쓰기 86 | Object.defineProperty(this, 'responseText', { value: JSON.stringify(responseData) }); 87 | } 88 | } 89 | }); 90 | } 91 | originalSend.apply(xhr, arguments); 92 | }; 93 | 94 | return xhr; 95 | } 96 | 97 | // 원본 XMLHttpRequest를 커스텀 XMLHttpRequest로 오버라이드 98 | unsafeWindow.XMLHttpRequest = CustomXHR; 99 | 100 | // 기본 스타일 추가 101 | GM.addStyle(` 102 | .star-container { 103 | position: absolute; 104 | top: 0; 105 | right: 0; 106 | opacity: 0; 107 | visibility: hidden; 108 | } 109 | .star-icon { 110 | line-height: normal; 111 | text-align: center; 112 | position: absolute; 113 | top: 5px; 114 | right: 5px; 115 | width: 18px; 116 | height: 18px; 117 | cursor: pointer; 118 | z-index: 10; 119 | font-size: 18px; 120 | font-family: Sandoll Nemony2, Apple SD Gothic NEO, Helvetica Neue, Helvetica, 나눔고딕, NanumGothic, Malgun Gothic, 맑은 고딕, 굴림, gulim, 새굴림, noto sans, 돋움, Dotum, sans-serif; 121 | text-shadow: 0px 1px 5px rgba(0, 0, 0, 0.3); 122 | transition: color 0.3s ease, opacity 0.3s ease; 123 | } 124 | .star-overlay { 125 | position: absolute; 126 | top: 0; 127 | right: 0; 128 | width: 100%; 129 | height: 100%; 130 | background: rgba(0, 0, 0, 0.5); 131 | justify-content: center; 132 | align-items: center; 133 | } 134 | [class^="component_item__"]:hover .star-container { 135 | opacity: 1; 136 | visibility: visible; 137 | } 138 | .pinned .star-container { 139 | opacity: 1; 140 | visibility: visible; 141 | } 142 | .pinned .star-icon { 143 | color: yellow; 144 | } 145 | .pinned .star-icon:hover { 146 | color: #ffe974; 147 | opacity: 0.5; 148 | } 149 | .star-icon.gray { 150 | color: gray !important; 151 | opacity: 0.5; 152 | } 153 | .star-icon.gray:hover { 154 | color: #ffe974 !important; 155 | opacity: 1; 156 | } 157 | 158 | .favorite-list-container { 159 | position: fixed; 160 | background: rgba(0,0,0,0.9); 161 | top: 0; 162 | left: 0; 163 | width: 100%; 164 | height: 100%; 165 | display: flex; 166 | flex-direction: column; 167 | justify-content: flex-start; 168 | align-items: center; 169 | z-index: 1000000000; 170 | font-size: 16px; 171 | color: var(--color-content-01); 172 | cursor: pointer; 173 | } 174 | .modal-title, .modal-footer, .close-message, .favorite-list-container table .name { 175 | font-family: Sandoll Nemony2, Apple SD Gothic NEO, Helvetica Neue, Helvetica, 나눔고딕, NanumGothic, Malgun Gothic, 맑은 고딕, 굴림, gulim, 새굴림, noto sans, 돋움, Dotum, sans-serif; 176 | } 177 | .favorite-list-container table { 178 | font-family: -apple-system, BlinkMacSystemFont, Apple SD Gothic Neo, Helvetica, Arial, NanumGothic, 나눔고딕, Malgun Gothic, 맑은 고딕, Dotum, 굴림, gulim, 새굴림, noto sans, 돋움, sans-serif; 179 | } 180 | .favorite-list-modal { 181 | position: relative; 182 | background: var(--color-bg-01); 183 | border: 1px solid var(--color-bg-02); 184 | padding: 20px; 185 | max-height: 80%; 186 | overflow-y: auto; 187 | cursor: default; 188 | margin-top: 10vh; 189 | } 190 | .favorite-list-item .name { 191 | min-width:220px; 192 | } 193 | .favorite-list-item .channelId { 194 | font-size: 12px; 195 | font-family: 'Consolas'; 196 | letter-spacing: -0.3px; 197 | } 198 | .favorite-list-table { 199 | width: 100%; 200 | border-collapse: collapse; 201 | } 202 | .favorite-list-table th, .favorite-list-table td { 203 | border: 1px solid var(--color-bg-02); 204 | padding: 5px 10px; 205 | text-align: center; 206 | } 207 | .favorite-list-table th { 208 | background: var(--color-bg-04); 209 | } 210 | .favorite-list-table ul { 211 | list-style-type: none; 212 | padding: 0; 213 | margin: 0; 214 | display: flex; 215 | width: 100%; 216 | } 217 | .favorite-list-table li { 218 | flex: 1; 219 | } 220 | .favorite-list-table button { 221 | margin: 0 5px; 222 | } 223 | .favorite-tab-setting-button { 224 | margin-left: auto; 225 | margin-right: 10px; 226 | } 227 | .modal-title { 228 | text-align: center; 229 | font-size: 20px; 230 | padding-bottom: 20px; 231 | } 232 | .modal-footer { 233 | font-size: 15px; 234 | padding-top: 20px; 235 | text-align: center; 236 | } 237 | .modal-footer a { 238 | color: var(--color-content-chzzk-01); 239 | text-decoration: none; 240 | position: relative; 241 | display: inline-block; 242 | transition: all 0.3s ease; 243 | padding-bottom: 3px; 244 | border-bottom: 3px solid rgba(0, 0, 0, 0); 245 | opacity: 0.8; 246 | } 247 | .modal-footer a:hover { 248 | padding-bottom: 3px; 249 | color: var(--color-content-chzzk-01); 250 | border-bottom: 3px solid var(--color-content-chzzk-01); 251 | opacity: 1; 252 | } 253 | .close-message { 254 | font-size: 15px; 255 | color: var(--color-content-04); 256 | margin: 15px 0; 257 | } 258 | [class^="following_container__"].starOnly [class^="component_item__"] { 259 | display: none; 260 | } 261 | [class^="following_container__"].starOnly [class^="component_item__"].pinned { 262 | display: block; 263 | } 264 | #LIVE { 265 | border-top-left-radius: 15px; 266 | border-bottom-left-radius: 15px; 267 | border-top-right-radius: 0px; 268 | border-bottom-right-radius: 0px; 269 | padding-right: 9px !important; 270 | } 271 | #FAVORITE_ONLY { 272 | border-top-left-radius: 0px; 273 | border-bottom-left-radius: 0px; 274 | border-top-right-radius: 15px; 275 | border-bottom-right-radius: 15px; 276 | margin-left: -4px !important; 277 | padding-left: 9px !important; 278 | padding-right: 9px !important; 279 | border-left: 1px solid var(--color-bg-01); 280 | } 281 | .favorite-star-button { 282 | font-size: 18px; 283 | text-align: center; 284 | padding: 12px !important; 285 | transition: color 0.3s ease, border-bottom 0.3s ease; 286 | position: relative; 287 | color: gray; 288 | } 289 | .favorite-star-button.favorite { 290 | color: yellow; 291 | } 292 | .favorite-star-button:hover { 293 | color: rgb(192, 192, 64) !important; 294 | } 295 | .favorite-star-button.favorite:hover { 296 | color: rgb(192, 192, 64) !important; 297 | } 298 | .favorite-star-button.favorite.no-hover:hover { 299 | color: yellow !important; 300 | } 301 | .favorite-star-button.no-hover:hover { 302 | color: gray !important; 303 | } 304 | 305 | 306 | 307 | 308 | 309 | :root { 310 | --color-content-chzzk-custom: 0, 255, 163; /* RGB 값 */ 311 | --color-content-chzzk-custom-gray: 127, 127, 127; /* RGB 값 */ 312 | } 313 | 314 | .ghost .star-icon.yellow { 315 | color: yellow !important; 316 | opacity: 1.0 !important; 317 | } 318 | .ghost .star-container { 319 | opacity: 1; 320 | visibility: visible; 321 | } 322 | /* 드래그 중인 엘리먼트에 적용될 애니메이션 */ 323 | @keyframes glowing { 324 | 0% { 325 | box-shadow: 0 0 3px rgba(var(--color-content-chzzk-custom), 0.3); 326 | background-color: rgba(var(--color-content-chzzk-custom), 0.05); 327 | } 328 | 50% { 329 | box-shadow: 0 0 10px rgba(var(--color-content-chzzk-custom), 0.7); 330 | background-color: rgba(var(--color-content-chzzk-custom), 0.15); 331 | } 332 | 100% { 333 | box-shadow: 0 0 3px rgba(var(--color-content-chzzk-custom), 0.3); 334 | background-color: rgba(var(--color-content-chzzk-custom), 0.05); 335 | } 336 | } 337 | 338 | /* 회색 빛나는 애니메이션 */ 339 | @keyframes glowing-gray { 340 | 0% { 341 | box-shadow: 0 0 3px rgba(var(--color-content-chzzk-custom-gray), 0.3); 342 | background-color: rgba(var(--color-content-chzzk-custom-gray), 0.05); 343 | } 344 | 50% { 345 | box-shadow: 0 0 10px rgba(var(--color-content-chzzk-custom-gray), 0.7); 346 | background-color: rgba(var(--color-content-chzzk-custom-gray), 0.15); 347 | } 348 | 100% { 349 | box-shadow: 0 0 3px rgba(var(--color-content-chzzk-custom-gray), 0.3); 350 | background-color: rgba(var(--color-content-chzzk-custom-gray), 0.05); 351 | } 352 | } 353 | 354 | /* 드래그 중인 엘리먼트에 적용할 클래스 */ 355 | .ghost [class*="video_card_thumbnail__"] { 356 | animation: glowing 1.0s infinite; 357 | } 358 | .ghost.ghost-gray [class*="video_card_thumbnail__"] { 359 | animation: glowing-gray 1.0s infinite; 360 | } 361 | 362 | 363 | 364 | 365 | 366 | `); 367 | 368 | ////////////////////////////// 369 | // parse class name 370 | ////////////////////////////// 371 | const classNameCache = {}; 372 | 373 | // 클래스 이름을 찾아주는 함수 374 | function getClassName(prefix) { 375 | if (classNameCache.hasOwnProperty(prefix)) { 376 | return classNameCache[prefix]; 377 | } 378 | 379 | const el = $(`[class*="${prefix}"]`).first(); 380 | if (el.length) { 381 | const className = getClassWithPrefix(el, prefix); 382 | classNameCache[prefix] = className; 383 | return className; 384 | } 385 | 386 | return `${prefix}not-found`; 387 | } 388 | 389 | // 특정 클래스 이름으로 시작하는 클래스를 가져오는 함수 390 | function getClassWithPrefix(element, prefix) { 391 | if (!element.length) return ""; 392 | return element[0].className.split(' ').find(cls => cls.startsWith(prefix)) || ""; 393 | } 394 | 395 | // 아이콘을 생성하는 함수 396 | function getNavigatorIcon(type) { 397 | if (type === 'refresh') { 398 | return ``; 399 | } else if (type === 'arrow') { 400 | return ``; 401 | } 402 | return ''; 403 | } 404 | 405 | ////////////////////////////// 406 | // Tab buttons 407 | ////////////////////////////// 408 | let starOnly = false; 409 | let liveBtnSimulated = false; 410 | 411 | // 즐겨찾기 버튼 추가 함수 412 | function addFavoriteTabButton(tabList) { 413 | console.log("addFavoriteTabButton"); 414 | starOnly = false; 415 | const followingContainer = $('[class^="following_container__"]'); 416 | if (!followingContainer.length) return; 417 | 418 | followingContainer.removeClass('starOnly'); 419 | 420 | const tabItemClass = tabList.children().filter((_, child) => $(child).attr('class').startsWith('button_tab_item__')).attr('class'); 421 | const favoriteTabButton = $('`); 618 | const $closebtn = $element.find(selectors.profileControlButton); 619 | $buttonContainer.append($mymemo); 620 | 621 | if(currentData.track){ 622 | if(currentData.chatLogs){ 623 | const $showChatLogs = $(``); 624 | $buttonContainer.append($showChatLogs); 625 | 626 | $showChatLogs.on("click", function() { 627 | $closebtn.trigger("click"); 628 | showChatLogsModal(uid); 629 | }); 630 | } 631 | 632 | if(currentData.nicknameHistory){ 633 | const $showNickHistory = $(``); 634 | $buttonContainer.append($showNickHistory); 635 | 636 | $showNickHistory.on("click", function() { 637 | $closebtn.trigger("click"); 638 | showNicknameHistoryModal(uid); 639 | }); 640 | } 641 | } 642 | 643 | if (currentData.nickname !== '') { 644 | $nickname.append(`[${currentData.nickname}]`); 645 | } 646 | 647 | $mymemo.on("click", function() { 648 | $closebtn.trigger("click"); 649 | openNoteModal(uid, nickname, currentData.nickname, currentData.note, currentData.track); 650 | }); 651 | }); 652 | 653 | GM.addStyle(` 654 | .view-all-memos-button { 655 | position: absolute; 656 | top: 10px; 657 | right: 30px; 658 | box-sizing: border-box; 659 | font-size: 10px; 660 | padding: 5px 10px; 661 | margin-right: 10px; 662 | background-color: var(--color-bg-04); 663 | border: 1px solid rgba(0, 0, 0, 1); 664 | color: var(--color-content-03); 665 | border-radius: 17px; 666 | cursor: pointer; 667 | font-family: Sandoll Nemony2, Apple SD Gothic NEO, Helvetica Neue, Helvetica, 나눔고딕, NanumGothic, Malgun Gothic, 맑은 고딕, 굴림, gulim, 새굴림, noto sans, 돋움, Dotum, sans-serif; 668 | } 669 | .view-all-memos-button:hover { 670 | color: var(--color-content-01); 671 | background-color: var(--color-bg-03); 672 | } 673 | 674 | .copy-uid-button { 675 | opacity: 0.01; 676 | position: absolute; 677 | top: 2px; 678 | right: 0; 679 | cursor: pointer; 680 | } 681 | .copy-uid-button:hover { 682 | opacity: 0.4; 683 | } 684 | .copied { 685 | color: var(--color-content-01); 686 | background-color: var(--color-bg-03); 687 | display:none; 688 | position:absolute; 689 | top:-30px; 690 | right:0px; 691 | font-size: 12px; 692 | box-sizing: border-box; 693 | padding: 7px 14px; 694 | border-radius: 17px; 695 | } 696 | 697 | .my-nickname { 698 | font-size: 0.8em; 699 | vertical-align: bottom; 700 | color: red; 701 | padding-left: 3px; 702 | font-weight: bold; 703 | } 704 | 705 | .modal-all-memo { 706 | z-index: 1000000; 707 | } 708 | .modal-user-nickname-history, .modal-chat-log { 709 | z-index: 10000000; 710 | } 711 | 712 | .modal-container { 713 | position: fixed; 714 | background: rgba(0, 0, 0, 0.8); 715 | top: 0; 716 | left: 0; 717 | width: 100%; 718 | height: 100%; 719 | display: flex; 720 | justify-content: center; 721 | align-items: center; 722 | flex-direction: column; 723 | z-index: 1000000; 724 | cursor: pointer; 725 | } 726 | .modal-content { 727 | position: relative; 728 | padding: 20px; 729 | max-width: 800px; 730 | width: 90%; 731 | cursor: default; 732 | background: var(--color-bg-01); 733 | border: 1px solid var(--color-bg-02); 734 | max-height: 80%; 735 | overflow-y: auto; 736 | } 737 | .modal-content table, .modal-content button { 738 | font-size: 12px; 739 | } 740 | .modal-header, .modal-content button, .copied { 741 | font-family:Sandoll Nemony2,Apple SD Gothic NEO,Helvetica Neue,Helvetica,나눔고딕,NanumGothic,Malgun Gothic,맑은 고딕,굴림,gulim,새굴림,noto sans,돋움,Dotum,sans-serif; 742 | } 743 | .modal-header { 744 | text-align: center; 745 | font-size: 18px; 746 | margin-bottom: 20px; 747 | } 748 | .modal-body input, .modal-body textarea { 749 | width: 100%; 750 | margin-bottom: 10px; 751 | padding: 10px; 752 | color: var(--color-content-01); 753 | background-color: var(--color-bg-01); 754 | border: 2px solid var(--color-bg-02); 755 | border-radius: 4px; 756 | } 757 | .modal-body input:focus, .modal-body textarea:focus { 758 | border: 2px solid var(--color-bg-06); 759 | outline: none; 760 | } 761 | .modal-footer { 762 | margin-top: 15px; 763 | text-align: center; 764 | } 765 | .modal-footer button { 766 | box-sizing: border-box; 767 | font-size: 14px; 768 | padding: 7px 14px; 769 | margin-right: 10px; 770 | background-color: var(--color-bg-04); 771 | border: 1px solid rgba(0,0,0,1); 772 | color: var(--color-content-03); 773 | border-radius: 17px; 774 | cursor: pointer; 775 | } 776 | /*.modal-footer button:last-child { 777 | background-color: #dc3545; 778 | }*/ 779 | .modal-footer button:hover, .modal-footer button:focus { 780 | color: var(--color-content-01); 781 | background-color: var(--color-bg-03); 782 | } 783 | 784 | .track-checkbox-wrapper { 785 | margin-bottom: 10px; 786 | } 787 | .track-checkbox-wrapper input { 788 | display: inline-block; 789 | width: 16px; 790 | height: 16px; 791 | vertical-align: text-top; 792 | margin-left:5px; 793 | } 794 | 795 | .all-memos-table { 796 | width: 100%; 797 | border-collapse: collapse; 798 | margin-top: 20px; 799 | } 800 | .all-memos-table th, .all-memos-table td { 801 | border: 1px solid var(--color-bg-02); 802 | padding: 3px 5px; 803 | text-align: center; 804 | } 805 | .all-memos-table th { 806 | white-space: nowrap; 807 | background: var(--color-bg-04); 808 | } 809 | 810 | 811 | .hompageLink { 812 | font-size:12px; 813 | margin-top:15px; 814 | padding:5px 0 0 0; 815 | } 816 | .hompageLink a { 817 | color: var(--color-content-chzzk-01); 818 | text-decoration: none; 819 | position: relative; 820 | display: inline-block; 821 | transition: all 0.3s ease; 822 | padding-bottom: 3px; 823 | border-bottom: 3px solid rgba(0, 0, 0, 0); 824 | opacity: 0.8; 825 | } 826 | .hompageLink a:hover { 827 | padding-bottom: 3px; 828 | color: var(--color-content-chzzk-01); 829 | border-bottom: 3px solid var(--color-content-chzzk-01); 830 | opacity: 1; 831 | } 832 | `); 833 | 834 | })(jQuery); 835 | -------------------------------------------------------------------------------- /CHZZK_User_Memo/README.md: -------------------------------------------------------------------------------- 1 | # CHZZK User Memo (Alpha Version) 2 | 3 | > **[공지] 테스트 버전이므로 별도 공지 없이 기능이 수정되거나 스크립트가 삭제될 수 있습니다.** 4 | 5 | - 치지직 채팅창에 유저 메모 기능을 추가합니다. 6 | - 악질 및 분탕 유저에 대한 관리 및 신고를 위한 목적으로 개발되었어요. 7 | - 누군가를 스토킹하는데 사용하지 않길 바라요! 8 | 9 | ## Preview 10 | 11 | 12 | 13 | - 메모가 존재하는 유저의 경우 닉네임 우측에 사용자가 설정한 별명을 표시해요. 14 | - 메모는 유저마다 고유한 UID를 기반으로 저장되므로, 닉네임이 변경되어도 유지돼요. 15 | - 사용자 이름을 클릭한 후 메모하기 버튼을 누르면 메모를 추가할 수 있어요. 16 | - 닉변 로그 또는 채팅 로그가 있는 경우 이를 확인하기 위한 버튼이 표시돼요. (추적 기능이 켜져있을 때) 17 | 18 | ### 메모 추가 19 | 20 | 21 | 22 | - 메모하기 버튼을 누르면 메모를 추가할 수 있어요. 23 | - 채팅창에 표시할 사용자 별명과, 상세 메모를 작성할 수 있어요. 24 | - 추적하기에 체크하는 경우, 채팅 로그와 닉변 로그를 저장해요. 25 | 26 | ### UID 복사 27 | 28 | 29 | 30 | - 채팅 가장 우측의 복사 버튼을 클릭하면 현재 닉네임, UID, 채팅 내용, 채팅 시간을 클립보드에 복사해요. 31 | 32 | ### 닉변 알림 33 | 34 | 35 | 36 | - 메모 되어있는 유저가 닉변한 후 채팅을 칠 때 알려줘요요. 37 | 38 | ### 메모 확인 39 | 40 | 41 | 42 | 43 | 44 | - 모든 메모 보기 버튼을 클릭하여 메모된 내용을 볼 수 있어요. 45 | 46 | ### 설정 47 | 48 | 49 | 50 | - 유저별 채팅과 닉변 로그를 저장할 개수를 설정할 수 있어요. 51 | - 로그가 지정된 수를 초과하는 경우 가장 오래된 것부터 삭제돼요. 52 | - 기본 값은 채팅 기록 30개, 닉변 기록 5개에요. 로그를 너무 많이 남기면 느려질 수도 있어요. 53 | 54 | ## Install 55 | 56 | 설치 방법을 설명합니다. 57 | 58 | ### STEP 1. ScriptManager 59 | 60 | 아래 리스트에서 본인이 사용 중인 브라우저에 맞는 링크에 접속한 후, 유저스크립트 관리 확장기능인 Tampermonkey 를 설치하세요. 61 | 62 | - Chrome - [Tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo) 63 | - Firefox - [Tampermonkey](https://addons.mozilla.org/ko/firefox/addon/tampermonkey/) 64 | - Opera - [Tampermonkey](https://addons.opera.com/extensions/details/tampermonkey-beta/) 65 | - Safari - [Tampermonkey](https://safari.tampermonkey.net/tampermonkey.safariextz) 66 | - Edge - [Tampermonkey](https://microsoftedge.microsoft.com/addons/detail/tampermonkey/iikmkjmpaadaobahmlepeloendndfphd) 67 | 68 | ### STEP 2. UserScript 69 | 70 | - 유저스크립트 관리 확장기능 설치 후 아래의 링크를 클릭하세요. 이후 뜨는 창에서 "설치" 버튼을 눌러 스크립트를 설치합니다. 71 | - [https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_User_Memo/CHZZK_User_Memo.user.js](https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_User_Memo/CHZZK_User_Memo.user.js) 72 | 73 | 이것으로 설치는 끝입니다. 즐겁게 사용하세요~ 74 | 75 | > 주의: 본 스크립트를 설치 및 사용하며 브라우저 과부하로 인한 응답 없음/뻗음으로 인한 데이터 손실이나 기타 발생하는 다른 문제에 대하여 개발자는 책임지지 않음(보고된 문제는 없음) 76 | > 본 스크립트는 Tampermonkey 외의 스크립트 매니저에서는 정상 동작하지 않을 수 있습니다. 77 | 78 | ## Note 79 | 80 | - 코드의 80% 이상을 ChatGPT로 작성했어요. 예외처리 같은 것이 제대로 안 되어 있긴 한데 현재는 대충 잘 되는 것 같아요. 문제가 생기면 스크립트를 비활성화 하세요. 81 | - 수초마다 닉변하면서 닌자 분신술을 펼치며 분탕칠을 치는 녀석이 있어서 만들었어요. 82 | 83 | ### 0.0.2 - Dec. 7, 2024 84 | 85 | - 채팅에서 닉네임 클릭 시 UID가 출력되지 않는 문제 수정 86 | 87 | ### 0.0.1 - Aug. 16, 2024 88 | 89 | - 최초 커밋 (Alpha 버전) 90 | 91 | ## License 92 | 93 | MIT 94 | 95 | ## Happy?? 96 | 97 | Buy Me A Coffee 98 | -------------------------------------------------------------------------------- /CHZZK_User_Memo/images/all_memo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomomo/Chzzk_Scripts/8c4abe463dc0cc6fe5d382ce76ca013498018c1b/CHZZK_User_Memo/images/all_memo.png -------------------------------------------------------------------------------- /CHZZK_User_Memo/images/button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomomo/Chzzk_Scripts/8c4abe463dc0cc6fe5d382ce76ca013498018c1b/CHZZK_User_Memo/images/button.png -------------------------------------------------------------------------------- /CHZZK_User_Memo/images/changenick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomomo/Chzzk_Scripts/8c4abe463dc0cc6fe5d382ce76ca013498018c1b/CHZZK_User_Memo/images/changenick.png -------------------------------------------------------------------------------- /CHZZK_User_Memo/images/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomomo/Chzzk_Scripts/8c4abe463dc0cc6fe5d382ce76ca013498018c1b/CHZZK_User_Memo/images/copy.png -------------------------------------------------------------------------------- /CHZZK_User_Memo/images/new_memo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomomo/Chzzk_Scripts/8c4abe463dc0cc6fe5d382ce76ca013498018c1b/CHZZK_User_Memo/images/new_memo.png -------------------------------------------------------------------------------- /CHZZK_User_Memo/images/setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomomo/Chzzk_Scripts/8c4abe463dc0cc6fe5d382ce76ca013498018c1b/CHZZK_User_Memo/images/setting.png -------------------------------------------------------------------------------- /CHZZK_User_Memo/images/usermenu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomomo/Chzzk_Scripts/8c4abe463dc0cc6fe5d382ce76ca013498018c1b/CHZZK_User_Memo/images/usermenu.png -------------------------------------------------------------------------------- /CHZZK_Video_RealTime/CHZZK_Video_RealTime.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name CHZZK Video RealTime 3 | // @namespace CHZZK_Video_RealTime 4 | // @version 0.0.1 5 | // @description Displays the actual time of the video when hovering over the slider bar on CHZZK replay videos 6 | // @author You 7 | // @match https://chzzk.naver.com/* 8 | // @supportURL https://github.com/nomomo/Chzzk_Scripts/issues 9 | // @homepage https://github.com/nomomo/Chzzk_Scripts/ 10 | // @downloadURL https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_Video_RealTime/CHZZK_Video_RealTime.user.js 11 | // @updateURL https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_Video_RealTime/CHZZK_Video_RealTime.user.js 12 | // @require https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.min.js 13 | // @grant GM_addStyle 14 | // ==/UserScript== 15 | 16 | (function () { 17 | 'use strict'; 18 | 19 | let debug = false; 20 | 21 | GM_addStyle(` 22 | .realtime-display { 23 | color: white; 24 | font-weight: 600; 25 | margin-top: 3px; 26 | font-size: 12px; 27 | text-shadow: 0 0 4px rgba(0,0,0,.4); 28 | } 29 | `); 30 | 31 | // Helper to parse time string (HH:MM:SS) to seconds 32 | function timeStringToSeconds(timeString) { 33 | const parts = timeString.split(':').map(Number); 34 | return parts.length === 3 35 | ? parts[0] * 3600 + parts[1] * 60 + parts[2] 36 | : parts[0] * 60 + parts[1]; 37 | } 38 | 39 | // Helper to format seconds as HH:MM:SS 40 | function secondsToTimeString(seconds) { 41 | const hours = Math.floor(seconds / 3600).toString().padStart(2, '0'); 42 | const minutes = Math.floor((seconds % 3600) / 60).toString().padStart(2, '0'); 43 | const secs = Math.floor(seconds % 60).toString().padStart(2, '0'); 44 | return `${hours}:${minutes}:${secs}`; 45 | } 46 | 47 | let liveOpenDate = ""; 48 | let totalDuration = 0; 49 | 50 | // Fetch video metadata and calculate total duration for the same liveOpenDate 51 | async function fetchVideoMetadata(videoNo) { 52 | const url = `https://api.chzzk.naver.com/service/v2/videos/${videoNo}`; 53 | try { 54 | const response = await fetch(url); 55 | const data = await response.json(); 56 | 57 | if (!data || !data.content) { 58 | console.error("Invalid response data:", data); 59 | return; 60 | } 61 | 62 | const content = data.content; 63 | if (debug) console.log(`Processing videoNo: ${content.videoNo}, Title: ${content.videoTitle}, liveOpenDate: ${content.liveOpenDate}, Duration: ${content.duration}`); 64 | 65 | // Check if the liveOpenDate matches 66 | if (liveOpenDate === "") { 67 | liveOpenDate = new Date(content.liveOpenDate); 68 | } 69 | else{ 70 | if (new Date(content.liveOpenDate).getTime() === liveOpenDate.getTime()) { 71 | // Accumulate duration 72 | totalDuration += content.duration; 73 | } 74 | else { 75 | if (debug) console.log("Different liveOpenDate encountered. Stopping accumulation."); 76 | return; 77 | } 78 | } 79 | 80 | // Process the next video recursively if it exists 81 | if (content.nextVideo) { 82 | await fetchVideoMetadata(content.nextVideo.videoNo); 83 | } 84 | } catch (error) { 85 | console.error("Error fetching video metadata:", error); 86 | } 87 | } 88 | 89 | // Main function to add realtime display 90 | function addRealtimeDisplay() { 91 | const videoNo = window.location.pathname.split('/').pop(); 92 | totalDuration = 0; // Reset duration 93 | liveOpenDate = ""; // Reset liveOpenDate 94 | fetchVideoMetadata(videoNo).then(() => { 95 | if (debug) console.log("Total accumulated duration:", totalDuration); 96 | }); 97 | } 98 | 99 | // Detect slider element and add event listeners 100 | document.arrive('div[role="slider"]', { existing: true }, function (slider) { 101 | function handleMouseMove() { 102 | if (liveOpenDate === "") return; 103 | 104 | const timerElement = document.querySelector('.pzp-seeking-preview__time'); 105 | if (timerElement) { 106 | const videoTimeText = timerElement.textContent.trim(); 107 | const videoTimeInSeconds = timeStringToSeconds(videoTimeText); 108 | 109 | // Calculate real-time with total duration offset 110 | const realTime = new Date(liveOpenDate.getTime() + (videoTimeInSeconds + totalDuration) * 1000); 111 | const formattedTime = realTime.toLocaleString(); 112 | 113 | // Create or update the realtime div 114 | let realtimeDiv = timerElement.nextElementSibling; 115 | if (!realtimeDiv || !realtimeDiv.classList.contains('realtime-display')) { 116 | realtimeDiv = document.createElement('div'); 117 | realtimeDiv.className = 'realtime-display'; 118 | timerElement.parentElement.appendChild(realtimeDiv); 119 | } 120 | realtimeDiv.textContent = `${formattedTime}`; 121 | } 122 | } 123 | 124 | slider.addEventListener('mousemove', handleMouseMove); 125 | }); 126 | 127 | // Detect URL changes using History API 128 | function observeUrlChanges() { 129 | let lastUrl = ""; 130 | 131 | const checkUrl = () => { 132 | const currentUrl = window.location.href; 133 | if (currentUrl !== lastUrl) { 134 | lastUrl = currentUrl; 135 | 136 | // Check if the new URL is a video page 137 | if (/https:\/\/chzzk\.naver\.com\/video\/\d+/.test(currentUrl)) { 138 | if (debug) console.log("URL changed to a video page:", currentUrl); 139 | addRealtimeDisplay(); 140 | return; 141 | } 142 | } 143 | 144 | liveOpenDate = ""; 145 | const element = document.querySelector('.realtime-display'); 146 | if (element) { 147 | element.style.display = 'none'; 148 | } 149 | }; 150 | 151 | // Override pushState and replaceState 152 | const originalPushState = history.pushState; 153 | const originalReplaceState = history.replaceState; 154 | 155 | history.pushState = function (...args) { 156 | originalPushState.apply(this, args); 157 | checkUrl(); 158 | }; 159 | 160 | history.replaceState = function (...args) { 161 | originalReplaceState.apply(this, args); 162 | checkUrl(); 163 | }; 164 | 165 | // Listen for popstate event 166 | window.addEventListener('popstate', checkUrl); 167 | 168 | // Initial check on first page load 169 | checkUrl(); 170 | } 171 | 172 | // Initialize 173 | observeUrlChanges(); 174 | })(); 175 | -------------------------------------------------------------------------------- /CHZZK_Video_RealTime/README.md: -------------------------------------------------------------------------------- 1 | # CHZZK Video RealTime 2 | 3 | - 이 스크립트는 CHZZK 다시보기 비디오의 슬라이더 바에 마우스를 올렸을 때, 비디오의 실제 시간을 표시해줍니다. 4 | - 동작 원리: 서버에서 가져온 다시보기의 라이브 시작 시간에 현재 마우스가 가리키는 시점의 동영상 시간을 더하여 계산된 시간을 표시합니다. 5 | 6 | ## Preview 7 | 8 | 9 | 10 | ## 설치 11 | 12 | 아래 단계를 따라 UserScript를 설치하세요. 13 | 14 | ### STEP 1. ScriptManager 15 | 16 | 먼저 아래 링크에서 본인이 사용 중인 브라우저에 맞는 Tampermonkey 확장 프로그램을 설치하세요. 17 | 18 | - Chrome - [Tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo) 19 | - Firefox - [Tampermonkey](https://addons.mozilla.org/ko/firefox/addon/tampermonkey/) 20 | - Opera - [Tampermonkey](https://addons.opera.com/extensions/details/tampermonkey-beta/) 21 | - Safari - [Tampermonkey](https://safari.tampermonkey.net/tampermonkey.safariextz) 22 | - Edge - [Tampermonkey](https://microsoftedge.microsoft.com/addons/detail/tampermonkey/iikmkjmpaadaobahmlepeloendndfphd) 23 | 24 | ### STEP 2. UserScript 25 | 26 | Tampermonkey 확장 프로그램 설치 후, 아래 링크를 클릭하세요. 팝업 창에서 "설치" 버튼을 눌러 스크립트를 설치합니다. 27 | 28 | - [https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_Video_RealTime/CHZZK_Video_RealTime.user.js](https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_Video_RealTime/CHZZK_Video_RealTime.user.js) 29 | 30 | 설치는 여기까지입니다. 즐겁게 사용하세요~ 31 | 32 | > 주의: 본 스크립트를 설치 및 사용하며 발생하는 브라우저 과부하로 인한 응답 없음, 뻗음으로 인한 데이터 손실이나 기타 문제에 대해 개발자는 책임지지 않습니다(보고된 문제는 없음). 33 | > 본 스크립트는 Tampermonkey 외의 스크립트 매니저에서는 정상 동작하지 않을 수 있습니다. 34 | 35 | ## 참고사항 36 | 37 | - 다시보기 비디오에 싱크 문제가 있거나 서버에서 가져오는 라이브 시작 시간에 문제가 있는 경우 표시되는 시간이 실제 시간과 다를 수 있으므로 참고 용으로만 사용하세요. 38 | - 라이브 시간이 길어 다시보기가 자동으로 잘린 경우 해당 라이브의 모든 다시보기는 동일한 라이브 시작 시간을 가집니다. 스크립트는 이전 다시보기의 정보를 체크하여 현재 보고있는 다시보기가 이전 다시보기에서 이어지는 것인지를 판단하고, 이를 고려하여 실제 시간을 계산합니다. 39 | - 만약 일부 다시보기가 숨겨진 경우 실제 시간이 잘못 계산될 수 있습니다. 40 | - 코드의 90% 이상을 ChatGPT로 작성했습니다. 비효율적인 부분이 있고 예외 처리가 제대로 되어 있지는 않지만 현재는 대체로 잘 동작합니다. 문제가 생기면 스크립트를 비활성화하세요. 41 | 42 | ### 0.0.1 - Jan 13, 2025 43 | 44 | - 최초 커밋 45 | 46 | ## 라이선스 47 | 48 | MIT 49 | 50 | ## 후원하기 51 | 52 | Buy Me A Coffee -------------------------------------------------------------------------------- /CHZZK_Video_RealTime/images/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nomomo/Chzzk_Scripts/8c4abe463dc0cc6fe5d382ce76ca013498018c1b/CHZZK_Video_RealTime/images/preview.png -------------------------------------------------------------------------------- /CHZZK_sign_in_iframe/CHZZK_sign_in_iframe.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name CHZZK_sign_in_iframe 3 | // @namespace CHZZK_sign_in_iframe 4 | // @version 0.0.2 5 | // @description iframe 으로 삽입된 CHZZK 페이지에서 로그인이 유지되도록 합니다. 6 | // @author Nomo 7 | // @match https://chzzk.naver.com/* 8 | // @supportURL https://github.com/nomomo/Chzzk_Scripts/issues 9 | // @homepage https://github.com/nomomo/Chzzk_Scripts/ 10 | // @downloadURL https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_sign_in_iframe/CHZZK_sign_in_iframe.user.js 11 | // @updateURL https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_sign_in_iframe/CHZZK_sign_in_iframe.user.js 12 | // @grant GM_cookie 13 | // @grant GM.cookie 14 | // @run-at document-start 15 | // ==/UserScript== 16 | 17 | (function () { 18 | 'use strict'; 19 | 20 | var DEBUG = false; 21 | const targetCookies = ["NID_SES", "NID_AUT", "NID_JKL"]; 22 | 23 | if (!GM.cookie) { 24 | console.log("GM.cookie is not supported."); 25 | return; 26 | } 27 | 28 | if (DEBUG) { 29 | GM.cookie.list({}).then(function (cookies, error) { 30 | console.log("get all coockies", cookies, error); 31 | }); 32 | } 33 | 34 | function setCookieSameSiteNone(targetCookieName, options) { 35 | GM.cookie.list({ 36 | name: targetCookieName 37 | }).then(function (cookies, error) { 38 | if (!error) { 39 | for (let i = 0; i < cookies.length; i++) { 40 | if(cookies[i].sameSite == "no_restriction" && cookies[i].secure == true){ 41 | continue; 42 | } 43 | 44 | // if (DEBUG) console.log("Try to delete old cookie", cookies[0]); 45 | // GM_cookie.delete(cookies[i], function () { 46 | 47 | // cookies[i].sameSite = "no_restriction"; 48 | // cookies[i].secure = true; 49 | 50 | // if (DEBUG) console.log("Try to set new cookie", cookies[0]); 51 | // GM.cookie.set(cookies[i]) 52 | // .then(function () { 53 | // if (DEBUG) console.log('set cookie done'); 54 | // }, function (error) { 55 | // if (DEBUG) console.log('set cookie error', error); 56 | // }); 57 | 58 | // }) 59 | 60 | cookies[i].sameSite = "no_restriction"; 61 | cookies[i].secure = true; 62 | 63 | if (DEBUG) console.log("Try to set new cookie", cookies[0]); 64 | GM.cookie.set(cookies[i]) 65 | .then(function () { 66 | if (DEBUG) console.log('set cookie done'); 67 | }, function (error) { 68 | if (DEBUG) console.log('set cookie error', error); 69 | }); 70 | } 71 | } 72 | else{ 73 | if (DEBUG) console.log('error from GM.cookie.list of setCookieSameSiteNone'); 74 | } 75 | }); 76 | } 77 | 78 | for (const ck of targetCookies) { 79 | setCookieSameSiteNone(ck); 80 | } 81 | 82 | let isTopWindow = window.self === window.top; 83 | 84 | // 쿠키 변경 시 갱신 85 | if (cookieStore) { 86 | cookieStore.addEventListener("change", function (event) { 87 | if (DEBUG) console.log("cookie change event", event.changed); 88 | 89 | if (event.changed.length > 0 && event.changed[0].sameSite != "none" && targetCookies.includes(event.changed[0].name)) { 90 | setCookieSameSiteNone(event.changed[0].name); 91 | } 92 | 93 | }); 94 | } else { 95 | console.log("cookieStore is not supported."); 96 | if(isTopWindow){ 97 | setInterval(function () { 98 | for (const ck of targetCookies) { 99 | setCookieSameSiteNone(ck); 100 | } 101 | }, 250); 102 | } 103 | } 104 | 105 | // 전혀 상관없는 다른 창에서 쿠키 변경된 경우 때문에 문제 발생하는 경우를 방지 106 | if(!isTopWindow){ 107 | document.addEventListener("DOMContentLoaded", function() { 108 | setTimeout(function(){ 109 | setInterval(function () { 110 | for (const ck of targetCookies) { 111 | setCookieSameSiteNone(ck); 112 | } 113 | }, 250); 114 | },2000); 115 | }); 116 | } 117 | 118 | })(); -------------------------------------------------------------------------------- /CHZZK_sign_in_iframe/README.md: -------------------------------------------------------------------------------- 1 | # CHZZK_sign_in_iframe 2 | 3 | - 본 스크립트는 iframe(embed)로 삽입된 CHZZK 페이지에서 로그인이 유지되도록 해줍니다. 4 | - 로그인이 유지되므로 iframe(embed)로 삽입된 CHZZK 페이지에서 연령 인증 스트림 시청, 채팅이 가능합니다. 5 | - 로그인은 [chzzk.naver.com](https://chzzk.naver.com) 에서 직접해야 합니다. 6 | - **본 스크립트는 Chrome 계열 브라우저의 경우 Tampermonkey Beta 버전에서만 동작합니다. (스크립트 매니저가 GM_cookie 기능을 지원해야 함)** 7 | 8 | ## 설치 방법 9 | 10 | ### STEP 1. Script Manager 설치 11 | 12 | **Chrome 계열 브라우저의 경우 Tampermonkey Beta 버전에서만 작동합니다.** 아래 링크에서 Tampermonkey Beta 를 설치하세요. 13 | 14 | 기존 Tampermonkey 사용자도 Tampermonkey Beta 버전을 추가로 설치해야 합니다. 15 | 16 | - Chrome - [Tampermonkey Beta](https://chromewebstore.google.com/detail/tampermonkey-beta/gcalenpjmijncebpfijmoaglllgpjagf) 17 | 18 | ### STEP 2. 개발자 모드 활성화 19 | 20 | - 2024년 5월부터 Tampermonkey를 사용하려면 개발자 모드를 활성화 해야합니다. 21 | - **chrome://extensions** 로 접속한 후, 확장 프로그램 관리 페이지 우측 상단의 "개발자 모드"를 켜세요. 22 | 23 | ### STEP 3. UserScript 설치 24 | 25 | - 유저스크립트 관리 확장기능 설치 후 아래의 링크를 클릭하세요. 이후 뜨는 창에서 "설치" 버튼을 눌러 스크립트를 설치합니다. 26 | - [https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_sign_in_iframe/CHZZK_sign_in_iframe.user.js](https://github.com/nomomo/Chzzk_Scripts/raw/main/CHZZK_sign_in_iframe/CHZZK_sign_in_iframe.user.js) 27 | 28 | 이것으로 설치는 끝입니다. 즐겁게 사용하세요~ 29 | 30 | > 주의: 본 스크립트를 설치 및 사용하며 브라우저 과부하로 인한 응답 없음/뻗음으로 인한 데이터 손실, 보안 이슈, 기타 발생하는 다른 문제에 대하여 개발자는 책임지지 않음(보고된 문제는 없음) 31 | > 본 스크립트는 Tampermonkey 외의 스크립트 매니저에서는 정상 동작하지 않을 수 있으며, Chrome 계열 브라우저의 경우 Tampermonkey Beta 버전에 스크립트가 설치되어야 합니다. 32 | 33 | ## 자주묻는 질문 34 | 35 | - Q: 안 돼요.
A: Chrome 계열 브라우저의 경우 Tampermonkey Beta 버전에 본 스크립트가 설치되었는지 확인하세요. 36 | - Q: 잘 되다가 갑자기 안 돼요.
A: 2024년 5월부터 Tampermonkey를 사용하려면 개발자 모드를 활성화 해야합니다. [chrome://extensions/](chrome://extensions/) 로 접속한 후, 우측 상단의 "개발자 모드"를 켜세요. 37 | - Q: 로그인은 어떻게 해요?
A: [chzzk.naver.com](https://chzzk.naver.com) 에서 하세요. 38 | - Q: 왜 Tampermonkey Beta 버전에 스크립트를 설치해야 하나요?
A: 기능 동작을 위해 스크립트 매니저에서 지원하는 GM_cookie 이라는 기능을 사용하여야 하는데, Chrome 계열 브라우저의 경우 Tampermonkey Beta 만 해당 기능을 지원합니다. 참고로 다른 브라우저의 경우 Tampermonkey 정식 버전에서도 GM_cookie 를 지원한다는데 테스트 해보지는 않았어요. 39 | - Q: 기존에 사용하던 Tampermonkey 정식 버전과 Beta 버전을 동시에 설치해서 사용해도 괜찮나요?
A: 괜찮습니다. 40 | - Q: 스크립트 설치할 때 Tampermonkey 정식 버전과 Beta 버전에서 설치하는 창이 둘 다 떠요.
A: 본 스크립트는 Tampermonkey Beta 버전에만 설치되어야 합니다. 만약 특정 버전에서 설치 페이지를 띄우는 것을 원하지 않는다면 다음을 따르세요.
[Tampermonkey 정식 or Beta 버전 대시보드] - [설정] - [설정 모드]를 상급자로 변경 - [스크립트 URL 감지] 를 비활성화됨으로 변경 - [저장] 버튼 클릭
이렇게 스크립트 URL 감지를 비활성화하여도 [대시보드] - [도구] - [Import from URL] 에 스크립트 주소를 붙여넣기 하고 설치 버튼을 눌러 스크립트를 설치할 수 있습니다. 41 | 42 | ## 알려진 버그 43 | 44 | - 간헐적으로 "로그인이 만료되었습니다. 페이지를 새로고침합니다." 오류가 발생할 수 있습니다. 45 | 46 | ### 0.0.2 - Jac. 13, 2024 47 | 48 | - "로그인이 만료되었습니다. 페이지를 새로고침합니다." 오류가 발생하는 빈도를 줄였어요. 49 | 50 | ### 0.0.1 - Jac. 06, 2024 51 | 52 | - 최초 커밋 53 | 54 | ## License 55 | 56 | MIT 57 | 58 | ## Happy?? 59 | 60 | Buy Me A Coffee -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Nomomo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chzzk_scripts 2 | 3 | 개인적인 용도로 사용하려고 만든 chzzk user-scripts 모음집 4 | 5 | - [Better Multichzzk](https://github.com/nomomo/Chzzk_Scripts/tree/main/Better_Multichzzk): 멀티치지직에서 자동으로 화면 넓게 6 | - [CHZZK sign in iframe](https://github.com/nomomo/Chzzk_Scripts/tree/main/CHZZK_sign_in_iframe): Embed(Iframe)으로 삽입된 곳에서도 로그인 유지 7 | - [CHZZK Live Progress Slider](https://github.com/nomomo/Chzzk_Scripts/tree/main/CHZZK_Live_Progress_Slider): 라이브 스트림 플레이어에 Progress Slider 를 표시 8 | - [CHZZK Always Awake](https://github.com/nomomo/Chzzk_Scripts/tree/main/CHZZK_Always_Awake): 탭이 비활성되어도 비활성 아닌척 함 9 | - [CHZZK Never Stop At Start](https://github.com/nomomo/Chzzk_Scripts/tree/main/CHZZK_Never_Stop_At_Start): 특정 확장기능이 라이브를 일시정지 시키는 문제를 고침 10 | - [CHZZK Favorite Streamer](https://github.com/nomomo/Chzzk_Scripts/tree/main/CHZZK_Favorite_Streamer): 즐겨찾는 팔로우 스트리머 설정 11 | - [CHZZK Restore Blind Chat](https://github.com/nomomo/Chzzk_Scripts/tree/main/CHZZK_Restore_Blind_Chat): 블라인드된 메시지 표시 12 | - [CHZZK User Memo](https://github.com/nomomo/Chzzk_Scripts/tree/main/CHZZK_User_Memo): 채팅창에 유저 메모 기능을 추가 (Alpha test version) 13 | - [CHZZK Max Quality](https://github.com/nomomo/Chzzk_Scripts/tree/main/CHZZK_Max_Quality): 최고 품질로 스트림 재생 시작 14 | - [CHZZK Hide Blind Chat VOD](https://github.com/nomomo/Chzzk_Scripts/tree/main/CHZZK_Hide_Blind_Chat_VOD): 치지직 다시보기(VOD) 에서 블라인드된 채팅 메시지를 숨김 15 | - [CHZZK Video RealTime](https://github.com/nomomo/Chzzk_Scripts/tree/main/CHZZK_Video_RealTime): CHZZK 다시보기 비디오의 슬라이더 바에 마우스를 올렸을 때, 비디오의 실제 시간을 표시 16 | 17 | ## 🪦 Deprecated Scripts 18 | 19 | 더 이상 사용하지 않거나 유지보수가 중단된 스크립트입니다. 20 | 21 | - ~~[CHZZK Better Shorts Player](https://github.com/nomomo/Chzzk_Scripts/tree/main/CHZZK_Better_Shorts_Player): 클립 보기 화면에 볼륨 컨트롤러 추가, 동작 개선~~ 22 | 23 | ## License 24 | 25 | MIT 26 | --------------------------------------------------------------------------------