Greasy Fork

Greasy Fork is available in English.

lck

롤캐 숙제 바로가기

目前为 2025-01-26 提交的版本,查看 最新版本

// ==UserScript==
// @name         lck
// @namespace    lck
// @version      1.8
// @description  롤캐 숙제 바로가기 
// @match        https://lolcast.kr/*
// @grant        GM_xmlhttpRequest
// ==/UserScript==

(function() {
    'use strict';

    // 채널 정보
    const CHANNELS = {
        youtube: {
            id: 'UCw1DsweY9b2AKGjV4kGJP1A',
            buttonLabel: '유튜브',
            color: '#FF0000',
            url: (id) => `/#/player/youtube/${id}`
        },
        chzzk: {
            buttonLabel: '치지직',
            color: '#00FFA3',
            url: () => '/#/player/chzzk/9381e7d6816e6d915a44a13c0195b202'
        },
        afreeca: {
            buttonLabel: '숲',
            color: '#2970B6',
            url: () => '/#/player/afreeca/aflol'
        },
        flow: {
            buttonLabel: 'Flow',
            color: '#6A5ACD', // 슬레이트블루 색상
            url: () => '/#/player/flow'
        }
    };

    // 유튜브 라이브 영상 ID 가져오기
    async function fetchLiveVideoId(channelId) {
        const YOUTUBE_LIVE_URL = `https://www.youtube.com/channel/${channelId}/live`;
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: YOUTUBE_LIVE_URL,
                onload: function(response) {
                    const videoIdMatch = response.responseText.match(/"videoId":"([\w-]+)"/);
                    const isLiveNow = response.responseText.includes('"isLiveNow":true') || response.responseText.includes('"isLive":true');
                    const liveBroadcastContentMatch = response.responseText.match(/"liveBroadcastContent":"(\w+)"/);
                    const isLiveBroadcast = liveBroadcastContentMatch && liveBroadcastContentMatch[1] === 'live';

                    if (videoIdMatch && videoIdMatch[1] && (isLiveNow || isLiveBroadcast)) {
                        resolve(videoIdMatch[1]);
                    } else {
                        reject('No live video found.');
                    }
                },
                onerror: reject
            });
        });
    }

    // 컨트롤 패널 생성
    const controlPanel = document.createElement('div');
    controlPanel.style.position = 'fixed';
    // 기본 위치 (left: 0, top: 380px) - 이후 드래그가 가능하므로 원하는 위치로 옮길 수 있습니다.
    controlPanel.style.top = '380px';
    controlPanel.style.left = '0';
    controlPanel.style.padding = '7px';
    controlPanel.style.borderRadius = '0 4px 4px 0';
    controlPanel.style.zIndex = '9999';
    controlPanel.style.display = 'flex';
    controlPanel.style.flexDirection = 'column'; // 세로 방향으로 배치
    controlPanel.style.gap = '6px';
    controlPanel.style.background = 'transparent';
    controlPanel.style.transition = 'all 0.3s ease';

    // 이전 위치 불러오기(localStorage)
    const savedPosition = localStorage.getItem('lckControlPanelPosition');
    if (savedPosition) {
        try {
            const { left, top } = JSON.parse(savedPosition);
            controlPanel.style.left = left;
            controlPanel.style.top = top;
        } catch(e) {
            console.error('Failed to parse saved control panel position:', e);
        }
    }

    // 드래그 기능 추가
    let isDragging = false;
    let offsetX = 0;
    let offsetY = 0;

    controlPanel.addEventListener('mousedown', (e) => {
        // 버튼 위에서도 이동 가능하도록 하려면, e.target이 버튼이면 return을 제거하거나 조정하세요.
        // 현재는 패널의 빈 영역에서만 드래그가 되는 것이 일반적이므로 조건을 사용하지 않습니다.
        isDragging = true;
        offsetX = e.clientX - controlPanel.offsetLeft;
        offsetY = e.clientY - controlPanel.offsetTop;
    });

    document.addEventListener('mousemove', (e) => {
        if (isDragging) {
            controlPanel.style.left = (e.clientX - offsetX) + 'px';
            controlPanel.style.top = (e.clientY - offsetY) + 'px';
        }
    });

    document.addEventListener('mouseup', () => {
        if (isDragging) {
            isDragging = false;
            // 위치 저장
            localStorage.setItem('lckControlPanelPosition', JSON.stringify({
                left: controlPanel.style.left,
                top: controlPanel.style.top
            }));
        }
    });

    // 버튼 생성 함수 (크기 1pt씩 작게 조정)
    function createChannelButton(channel) {
        const button = document.createElement('button');
        button.textContent = channel.buttonLabel;
        button.style.padding = '5px 11px';
        button.style.color = channel.color;
        button.style.border = `1px solid ${channel.color}`;
        button.style.borderRadius = '3px';
        button.style.cursor = 'pointer';
        button.style.fontSize = '13px';
        button.style.transition = 'all 0.2s';
        button.style.background = 'transparent';

        button.onmouseover = () => {
            button.style.background = channel.color;
            button.style.color = 'white';
        };
        button.onmouseout = () => {
            button.style.background = 'transparent';
            button.style.color = channel.color;
        };
        button.onclick = async () => {
            if (channel.buttonLabel === '유튜브') {
                try {
                    const liveVideoId = await fetchLiveVideoId(channel.id);
                    window.location.href = channel.url(liveVideoId);
                } catch {
                    alert('유튜브 라이브 방송이 없습니다.');
                }
            } else {
                window.location.href = channel.url();
            }
        };
        return button;
    }

    // 토글 버튼 생성 (크기 1pt씩 작게 조정)
    function createToggleButton() {
        const toggleButton = document.createElement('button');
        toggleButton.textContent = '◀';
        toggleButton.style.padding = '5px 11px';
        toggleButton.style.color = '#888';
        toggleButton.style.border = '1px solid #888';
        toggleButton.style.borderRadius = '3px';
        toggleButton.style.cursor = 'pointer';
        toggleButton.style.fontSize = '13px';
        toggleButton.style.transition = 'all 0.2s';
        toggleButton.style.background = 'transparent';

        toggleButton.onmouseover = () => {
            toggleButton.style.background = '#888';
            toggleButton.style.color = 'white';
        };
        toggleButton.onmouseout = () => {
            toggleButton.style.background = 'transparent';
            toggleButton.style.color = '#888';
        };
        toggleButton.onclick = () => {
            controlPanel.style.display = 'none'; // 패널 숨기기
        };
        return toggleButton;
    }

    // 버튼 추가
    const youtubeButton = createChannelButton(CHANNELS.youtube);
    const chzzkButton = createChannelButton(CHANNELS.chzzk);
    const afreecaButton = createChannelButton(CHANNELS.afreeca);
    const flowButton = createChannelButton(CHANNELS.flow); // flow 버튼 추가
    const toggleButton = createToggleButton();

    // 첫 번째 줄: 유튜브, 치지직, 숲
    const firstLine = document.createElement('div');
    firstLine.style.display = 'flex';
    firstLine.style.gap = '6px';
    firstLine.appendChild(youtubeButton);
    firstLine.appendChild(chzzkButton);
    firstLine.appendChild(afreecaButton);

    // 두 번째 줄: Flow, 토글 버튼
    const secondLine = document.createElement('div');
    secondLine.style.display = 'flex';
    secondLine.style.gap = '6px';
    secondLine.appendChild(flowButton);
    secondLine.appendChild(toggleButton);

    // 패널에 줄 추가
    controlPanel.appendChild(firstLine);
    controlPanel.appendChild(secondLine);
    document.body.appendChild(controlPanel);
})();