Killing Kick Latency

kick 레이턴시 줄이기

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==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);
})();