디시인사이드 갤러리

최근 방문

NEW

갤러리 이슈박스, 최근방문 갤러리

갤러리 본문 영역

[📕공유] 소프트콘 확프 튜닝버전 공유

ㅇㅇ 2026.03.11 20:25:47
조회 156 추천 0 댓글 0
Video Player is loading.
Current Time 0:11
Duration 1:21
Loaded: 32.58%
Stream Type LIVE
Remaining Time 1:10
 
1x
														

// ==UserScript==

// @name         소프트콘 실청자 계산기 (비정상 표시 추가)

// @description  숲 시청자에 피모비율 적용, 정렬 및 PC 비율 과다 시 비정상 표시

// @namespace    https://tampermonkey.net/

// @version      1.2.0

// @license      MIT

// @match        *://viewership.softc.one/*

// @grant        none

// ==/UserScript==


(function () {

    'use strict';


    // 설정 값

    const OFFICIAL_PC = 30;

    const OFFICIAL_MOBILE = 70;

    const ABNORMAL_THRESHOLD = 30; // PC 비율이 몇 %를 넘으면 '비정상'으로 띄울지 설정


    };


    const AFREECA_SELECTOR = 'a[href^="/channel/afreeca/"]';

    let obs = null;

    let cooldown = false;


    const parseIntStrict = (txt) => (txt || '').replace(/[^0-9]/g, '') * 1 || 0;

    const extractTwoPercents = (txt) => {

        const m = txt.match(/(\d+)\s*%/g);

        return m?.length >= 2 ? m.map(v => parseInt(v)).slice(0, 2) : null;

    };


    const calcRealTotal = (total, pcPct, mobilePct, targetPc, targetMobile) => {

        const realMobile = total * (mobilePct / 100);

        const estPC = realMobile * (targetPc / targetMobile);

        return Math.round(realMobile + estPC);

    };


    // "비정상" 배지 엘리먼트 생성 함수

    const createAbnormalBadge = () => {

        const badge = document.createElement('span');

        badge.textContent = '비정상';

        badge.style.backgroundColor = '#ef4444'; // 빨간색 (Tailwind red-500)

        badge.style.color = 'white';

        badge.style.fontSize = '11px';

        badge.style.padding = '1px 4px';

        badge.style.borderRadius = '4px';

        badge.style.marginRight = '5px';

        badge.style.fontWeight = 'bold';

        badge.style.verticalAlign = 'middle';

        return badge;

    };


    const findLiveRankingContainer = () => {

        const headers = Array.from(document.querySelectorAll('h2, h3, h4'));

        for (const h of headers) {

            if (/실시간\s*방송/i.test(h.textContent)) {

                const next = h.nextElementSibling;

                if (next && next.querySelector('a[href^="/channel/"]')) return next;

            }

        }

        return document.querySelector('div.text-sm.font-medium.flex.flex-col.gap-2.justify-center');

    };


    const processCard = (a) => {

        if (!a) return;

        const wrap = a.querySelector('.flex.flex-col.pr-2.leading-none.gap-1');

        if (!wrap) return;

        const viewerDiv = wrap.querySelector('div.font-bold, div.text-right');

        if (!viewerDiv) return;


        let text = viewerDiv.textContent.replace(/\(\s*\d[\d,]*\s*명?\s*\)/g, '').replace(/[^\d,]/g, '').trim();

        const total = parseIntStrict(text);

        if (!total) return;


        let score = total;


        if (a.matches(AFREECA_SELECTOR)) {

            const href = a.getAttribute('href') || '';

            const streamerId = href.split('/').pop();


            let targetPc = OFFICIAL_PC;

            let targetMobile = OFFICIAL_MOBILE;

            if (CUSTOM_RATIOS[streamerId]) {

                targetPc = CUSTOM_RATIOS[streamerId].pc;

                targetMobile = CUSTOM_RATIOS[streamerId].mobile;

            }


            const divs = wrap.querySelectorAll('div');

            let ratioDiv = null;

            for (const d of divs) {

                if (d.querySelectorAll('svg').length >= 2 && d.textContent.includes('%')) {

                    ratioDiv = d;

                    break;

                }

            }


            if (ratioDiv) {

                const perc = extractTwoPercents(ratioDiv.textContent);

                if (perc) {

                    if (viewerDiv.textContent.includes('→')) return;


                    const [pc, mobile] = perc;

                    const real = calcRealTotal(total, pc, mobile, targetPc, targetMobile);

                    score = real;


                    if (!viewerDiv.hasAttribute('data-realviewer-added')) {

                        const spanNode = viewerDiv.querySelector('span');

                        if (!spanNode) return;


                        const origText = viewerDiv.childNodes[0]?.textContent.trim() || '';

                        viewerDiv.textContent = '';


                        // 1. PC 비율이 임계값을 넘으면 비정상 배지 추가

                        if (pc > ABNORMAL_THRESHOLD) {

                            viewerDiv.appendChild(createAbnormalBadge());

                        }


                        // 2. 기존 시청자수 (흐리게)

                        const oldSpan = document.createElement('span');

                        oldSpan.className = 'text-xs opacity-70';

                        oldSpan.textContent = `${origText} →`;

                        viewerDiv.appendChild(oldSpan);


                        // 3. 계산된 실청자수

                        const realText = document.createTextNode(` ${real.toLocaleString()}`);

                        viewerDiv.appendChild(realText);


                        // 4. '명' 단위

                        const newSpan = spanNode.cloneNode(true);

                        viewerDiv.appendChild(newSpan);


                        viewerDiv.setAttribute('data-realviewer-added', '1');

                    }

                }

            }

        }

        a.dataset.realScore = score;

    };


    const restartObserver = () => {

        if (cooldown) return;

        cooldown = true;

        setTimeout(() => {

            if (obs) obs.observe(document.body, { childList: true, subtree: true });

            cooldown = false;

        }, 1000);

    };


    const scanAndSort = () => {

        const container = findLiveRankingContainer();

        if (!container) return;

        if (obs) obs.disconnect();


        const cards = Array.from(container.querySelectorAll('a[href^="/channel/"]'));

        if (!cards.length) {

            restartObserver();

            return;

        }


        cards.forEach(processCard);


        const sorted = cards.sort((a, b) => {

            const va = parseInt(a.dataset.realScore || 0);

            const vb = parseInt(b.dataset.realScore || 0);

            return vb - va;

        });


        const moreBtn = container.querySelector('button.font-bold.flex.justify-center');

        if (moreBtn) moreBtn.remove();


        sorted.forEach((el, i) => {

            const rankDiv = el.querySelector('div.w-3.text-center');

            if (rankDiv) rankDiv.textContent = (i + 1).toString();

            container.appendChild(el);

        });


        if (moreBtn) container.appendChild(moreBtn);

        restartObserver();

    };


    obs = new MutationObserver((mutations) => {

        const added = mutations.flatMap(m => Array.from(m.addedNodes)).filter(n => n.nodeType === 1);

        if (added.length) {

            if (scanAndSort._raf) cancelAnimationFrame(scanAndSort._raf);

            scanAndSort._raf = requestAnimationFrame(scanAndSort);

        }

    });


    obs.observe(document.body, { childList: true, subtree: true });

    setTimeout(scanAndSort, 1500);

})();


자동등록방지

추천 비추천

0

고정닉 0

0

댓글 영역

전체 댓글 0
본문 보기
자동등록방지

하단 갤러리 리스트 영역

왼쪽 컨텐츠 영역

갤러리 리스트 영역

갤러리 리스트
번호 말머리 제목 글쓴이 작성일 조회 추천
- 설문 공항 비매너 논란 때문에 호감도 떨어진 스타는? 운영자 26/07/06 - -
- AD 지친 입맛을 깨워줄 보양식! 운영자 26/03/05 - -
12525519 공지 신문고
ㅇㅇ
26.07.07 3323 3
5320909 공지 신문고 공지사항 (답글 금지)
ㅇㅇ
25.08.14 101107 51
12539881 ®️A 리액트 근본은 이거지
ㅇㅇ
21:56 3 0
12539882 📕공유 Official ) The Global No.1 Vtuber Group
ㅇㅇ
21:56 8 0
12539880 일반 굴포차 이제 고소될텐데 [1]
ㅇㅇ
21:56 17 0
12539879 일반 낑낑이 나이가있어서그런가 섹스어필 꾸준하네
ㅇㅇ
21:56 8 0
12539878 일반 리제방 채팅 수위 어느정도임? [1]
ㅇㅇ
21:56 13 0
12539877 일반 레나 팰월드 따로함? [4]
ㅇㅇ
21:56 21 0
12539876 일반 버타쿠 이야기 모르는 일반인들은
ㅇㅇ
21:56 15 0
12539875 일반 12명 합방은 음
ㅇㅇ
21:56 9 0
12539874 일반 그래서 리사는 오늘 겜 머함
ㅇㅇ
21:55 4 0
12539873 일반 근데 브이럽 안터졌는데 [2]
ㅇㅇ
21:55 24 0
12539872 일반 레나야 버챈트한테 낙수좀 뿌려줘
ㅇㅇ
21:55 10 0
12539871 ®️A 본토 하꼬박이들은 또 뭐야 습박 [1]
ㅇㅇ
21:55 22 0
12539870 ®️A ㅅㅂ 낑낑이 채팅에 보x키스데이는 뭐냐 ㅋㅋㅋㅋㅋㅋㅋ
ㅇㅇ
21:55 36 0
12539869 일반 내년만 되도 레나가 인챈트 1황 될 듯
ㅇㅇ
21:55 8 0
12539868 일반 근데 리제 판뗴기는 ㄹㅇ 볼때마다 개잘어울림
ㅇㅇ
21:55 27 0
12539867 일반 당연하 굴 거리는 고소되지
ㅇㅇ
21:55 28 0
12539866 일반 수직갤 주장 : 찐요는 고소안됨 굴은 고소됨
ㅇㅇ
21:54 37 2
12539865 일반 약 좀 먹어라 이년아
ㅇㅇ
21:54 23 0
12539864 일반 버튜버도 메이플 충분히 즐길만하지
ㅇㅇ
21:54 14 0
12539863 일반 레나 휴방인데 하루죙일 벽딸이노
ㅇㅇ
21:54 10 0
12539862 일반 레나 억까 심해진 이유가 뭐냐 [2]
ㅇㅇ
21:54 28 0
12539861 일반 헤비 저거 언지까지함? 6분남았는데
ㅇㅇ
21:54 34 0
12539860 ®️A 방송시간 10시간 이상 완방은 진짜 빡세다
ㅇㅇ
21:54 23 0
12539859 일반 버튜버에 돈 쓰지말고 주식해라 [9]
ㅇㅇ
21:53 84 0
12539858 일반 근데 버튜버들은 레나를 멀리해야 정상 아니냐
ㅇㅇ
21:53 24 0
12539857 📕공유 ⚠+혹시 이분들은 제미나이에 뇌를 의탁하셨나요? [4]
ㅇㅇ
21:53 76 1
12539856 일반 로크가 조이 하드카운터네 ㅋㅋ [4]
ㅇㅇ
21:53 37 0
12539855 일반 레나는 빨대로 체급 키워놓고 낙수는 1도 안뿌리네
ㅇㅇ
21:53 17 0
12539854 일반 초반에는 빡시게 처벌한다 사려라
ㅇㅇ
21:53 21 0
12539853 일반 머독 이새끼 뭐임 ㅋㅋㅋㅋㅋ [7]
ㅇㅇ
21:53 212 2
12539852 일반 레나 이새기 유행어 뻇어가서 빡침 [7]
ㅇㅇ
21:53 69 0
12539851 일반 히나 좀 짠하긴하네 나름 공지방송이였는데 [2]
ㅇㅇ
21:52 90 0
12539850 일반 굴은 고소되는데 찐요는 고소안된다고 믿는 수직갤왔노? [2]
ㅇㅇ
21:52 32 0
12539849 일반 헤비는 왜 효니나 도랄팤 하고는 안놀아줌ㅜ [9]
ㅇㅇ
21:52 67 0
12539848 일반 레나 좀 뜨니까 또 억까하면서 까내리는 정병 찾아왔나봄
ㅇㅇ
21:52 20 0
12539847 일반 탬탬은 요즘 유행인건 다 건드려서 범접불가임 [1]
ㅇㅇ
21:52 52 0
12539846 일반 하시요 롤 잘하네. [10]
ㅇㅇ
21:52 69 1
12539845 🌌씨미 엘라 최강릴파 우승자 출신임 [2]
ㅇㅇ
21:52 38 2
12539844 일반 난 진짜 버투버는 레나 젤많이보는데 [4]
ㅇㅇ
21:52 53 0
12539842 📕공유 펨숲이 진짜 쇄국이긴하네
ㅇㅇ
21:52 59 0
12539841 일반 이갤 첨해봤을때 다들 자동삭제쓰는줄알았음 [4]
ㅇㅇ
21:52 42 0
12539840 일반 레나 << 얘는 그래서 낙수 언제 뿌림? [2]
ㅇㅇ
21:52 33 1
12539839 일반 부키 메이플 잠깐만키고 메인겜은 딴거 한다면서 [3]
ㅇㅇ
21:52 81 1
12539838 일반 확실히 버튜버들은 영화보단 애니긴하네 [1]
ㅇㅇ
21:51 25 0
12539837 🧓 근대 스갤시절처럼 " " << 이러케 언급하면 [19]
ㅇㅇ
21:51 89 2
12539836 ®️A 10분.. 10분 뒤 리액트에 자연재해가 온다 [1]
ㅇㅇ
21:51 60 0
12539834 일반 레나가 무서운게 합방하고나면 상대방 시청자 다 뺏어옴
ㅇㅇ
21:51 28 0
12539833 일반 해산물 이름인데? < 이게 안통하는 이유 [9]
ㅇㅇ
21:51 99 0
갤러리 내부 검색
제목+내용게시물 정렬 옵션

오른쪽 컨텐츠 영역

실시간 베스트

1/8

디시미디어

디시이슈

1/4