Killing Kick Latency

kick 레이턴시 줄이기

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         Killing Kick Latency
// @namespace    http://tampermonkey.net/
// @version      1.01
// @description  kick 레이턴시 줄이기
// @author       purirun
// @match        https://www.kick.com/*
// @match        https://kick.com/*
// @grant        none
// @run-at       document-idle
// @license MIT
// ==/UserScript==

(function () {
  const VIDEO_SEL = 'video';
  let delayText = null;
  let isCooling = false;
  let chaseInterval = null;

  function getControlBar() {
    const all = document.querySelectorAll('.z-controls');
    // absolute bottom-0 인 컨트롤바
    return [...all].find(el => el.classList.contains('bottom-0'));
  }

  function createSyncBtn() {
    if (document.getElementById('kick-tiny-sync')) return;

    const bar = getControlBar();
    if (!bar) return;

    // index 1 = 오른쪽 버튼 그룹
    const rightGroup = bar.children[1];
    if (!rightGroup) return;

    const syncBtn = document.createElement('div');
    syncBtn.id = 'kick-tiny-sync';
    syncBtn.title = '클릭하면 딜레이가 즉시 동기화됩니다.';
    syncBtn.style.cssText = [
      'display:inline-flex',
      'align-items:center',
      'gap:5px',
      'background:rgba(255,255,255,0.1)',
      'border:1px solid transparent',
      'border-radius:6px',
      'padding:3px 8px',
      'cursor:pointer',
      'user-select:none',
      'transition:all 0.2s',
      'font-family:monospace',
      'height:32px',
      'box-sizing:border-box',
      'flex-shrink:0',
    ].join(';');

    syncBtn.onmouseover = () => {
      syncBtn.style.background = 'rgba(255,255,255,0.2)';
      syncBtn.style.border     = '1px solid #53FC18';
    };
    syncBtn.onmouseout = () => {
      if (!isCooling) {
        syncBtn.style.background = 'rgba(255,255,255,0.1)';
        syncBtn.style.border     = '1px solid transparent';
      }
    };

    const icon = document.createElement('span');
    icon.innerText      = '⚡';
    icon.style.fontSize = '12px';

    delayText = document.createElement('span');
    delayText.innerText     = '-.-s';
    delayText.style.cssText = 'font-size:12px;font-weight:bold;color:#fff;line-height:1;white-space:nowrap;';

    syncBtn.appendChild(icon);
    syncBtn.appendChild(delayText);

    syncBtn.onclick = (e) => {
      e.preventDefault();
      e.stopPropagation();
      if (isCooling) return;

      const vid = document.querySelector(VIDEO_SEL);
      if (!vid) return;

      // 버퍼 끝으로 점프
      if (vid.buffered.length > 0) {
        vid.currentTime = vid.buffered.end(vid.buffered.length - 1) - 0.3;
      } else {
        vid.currentTime = vid.duration;
      }

      // 딜레이 남아있으면 1.3배속으로 추격
      if (chaseInterval) clearInterval(chaseInterval);
      chaseInterval = setInterval(() => {
        const v = document.querySelector(VIDEO_SEL);
        if (!v) { clearInterval(chaseInterval); return; }
        const delay = v.buffered.length > 0
          ? v.buffered.end(v.buffered.length - 1) - v.currentTime
          : 0;
        if (delay > 1) {
          v.playbackRate = 1.3;
        } else {
          v.playbackRate = 1.0;
          clearInterval(chaseInterval);
        }
      }, 200);

      isCooling = true;
      delayText.innerText   = 'OK!';
      delayText.style.color = '#53FC18';
      syncBtn.style.border  = '1px solid #53FC18';

      setTimeout(() => {
        isCooling = false;
        syncBtn.style.border     = '1px solid transparent';
        syncBtn.style.background = 'rgba(255,255,255,0.1)';
      }, 1500);
    };

    // 오른쪽 그룹의 첫 번째 버튼 앞에 삽입
    const firstBtn = rightGroup.querySelector('button');
    if (firstBtn) {
      rightGroup.insertBefore(syncBtn, firstBtn);
    } else {
      rightGroup.appendChild(syncBtn);
    }
  }

  // 딜레이 수치 갱신
  setInterval(() => {
    if (!document.getElementById('kick-tiny-sync')) {
      delayText = null;
      createSyncBtn();
      return;
    }
    if (!delayText || isCooling) return;

    const vid = document.querySelector(VIDEO_SEL);
    if (!vid || vid.paused) {
      delayText.innerText   = '---';
      delayText.style.color = '#888';
      return;
    }

    if (vid.buffered.length > 0) {
      const delay = Math.max(0, vid.buffered.end(vid.buffered.length - 1) - vid.currentTime);
      delayText.innerText   = delay.toFixed(1) + 's';
      delayText.style.color = delay < 1.5 ? '#53FC18' : delay < 3 ? '#FFD700' : '#FF4D4D';
    } else {
      delayText.innerText   = '???';
      delayText.style.color = '#888';
    }
  }, 500);

  // 초기 생성 - 컨트롤바는 hover시에만 나타나므로 계속 재시도
  let attempts = 0;
  const initInterval = setInterval(() => {
    if (document.body) {
      createSyncBtn();
      if (document.getElementById('kick-tiny-sync')) clearInterval(initInterval);
    }
    if (++attempts >= 40) clearInterval(initInterval);
  }, 500);
})();