고려대학교 블랙보드 트윅

고려대학교 블랙보드의 녹화강의 출석현황을 보거나 영상을 다운로드하거나 블랙보드의 성적을 확인 합니다.

// ==UserScript==
// @name         고려대학교 블랙보드 트윅
// @namespace    https://kulms.korea.ac.kr/
// @version      0.4
// @description  고려대학교 블랙보드의 녹화강의 출석현황을 보거나 영상을 다운로드하거나 블랙보드의 성적을 확인 합니다.
// @author       Meda
// @match        https://kulms.korea.ac.kr/webapps/blackboard/*
// @match        https://kucom.korea.ac.kr/em/*
// @match        https://kulms.korea.ac.kr/ultra/stream/telemetry/student/high-performance/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=ac.kr
// @grant        GM_xmlhttpRequest
// @license      MIT
// ==/UserScript==


function gradeCheckFunc() {
    var currentUrl = window.location.href;
    if(currentUrl.includes('course_id=')){
        var urlPoint = currentUrl.indexOf('course_id=');
        var courseId = currentUrl.substring(urlPoint+10, urlPoint+19);

        document.querySelector("#courseMenuPalette_contents").insertAdjacentHTML('afterbegin', `<li id="paletteItem:" class="clearfix ">
                                     <a href="/ultra/stream/telemetry/student/high-performance/`+courseId+`">
                                     <span title="내가 잘 하고 있습니까?">내가 잘 하고 있습니까?</span></a></li>`);
    }
}

if (window.location.href.startsWith('https://kulms.korea.ac.kr/webapps/blackboard/')) {
    gradeCheckFunc();
}


function attendanceCheckFunc() {
    var currentUrl = window.location.href;
    if(currentUrl.includes('course_id=')){
        var urlPoint = currentUrl.indexOf('course_id=');
        var courseId = currentUrl.substring(urlPoint+10, urlPoint+19);

        document.querySelector("#courseMenuPalette_contents").insertAdjacentHTML('afterbegin', `<li id="paletteItem:" class="clearfix ">
                                     <a href="/webapps/blackboard/execute/blti/launchPlacement?blti_placement_id=_136_1&course_id=`+courseId+`&mode=view" target="_self">
                                     <span title="녹강 출결 확인">녹강 출석 확인</span></a></li>`);
    }
}

if (window.location.href.startsWith('https://kulms.korea.ac.kr/webapps/blackboard/')) {
    attendanceCheckFunc();
}


function viewMygradeFunc(courseId) {
    // .full.activity-grade 요소를 찾을 때까지 0.1초마다 검색
    var grade = -1; // 찾은 "grade" 값을 저장할 변수

    var gradesArray = []; // grade 값을 저장할 배열 생성
    var i, item;
    var interval = setInterval(function() {
        var element = document.querySelector('.full.activity-grade');

        if (element) {
            clearInterval(interval); // 검색 간격 멈추기
            fetch('https://kulms.korea.ac.kr/learn/api/v1/courses/'+courseId+'/telemetry/reports/activityVsGrade')
                .then(response => response.json())
                .then(parsedData => {

                for (var i = 0; i < parsedData.data.length; i++) {
                    var item = parsedData.data[i];
                    if (item.membership) {
                        // membership이 존재하는 경우
                        grade = parseFloat(item.grade); // 나의 grade 값을 추가
                        gradesArray.push(parseFloat(item.grade)); // grade 값을 배열에 추가
                    }else if (!item.grade) {
                        // grade가 존재하는않는 경우
                        grade = 0.0;
                    }else{
                        gradesArray.push(parseFloat(item.grade)); // grade 값을 배열에 추가
                    }
                }

                gradesArray.sort(function(a, b) {
                    return b - a; // 내림차순으로 정렬
                });

                var index = gradesArray.indexOf(grade)+1;
                var itemCount = gradesArray.length;

                // .full.activity-grade 요소 선택
                var parentElement = document.querySelector('.full.activity-grade');

                var newHeading = document.createElement('h2');
                newHeading.style.fontWeight = 'bold';

                var gradeText = document.createElement('div');
                var rankText = document.createElement('div');

                if (grade == -1) {
                    gradeText.textContent = '나의 성적: --%';
                    rankText.textContent = '나의 석차: ' + itemCount + '명 중 --등  (상위: --%)';
                } else {
                    gradeText.textContent = '나의 성적: ' + (grade * 100).toFixed(3) + '%';
                    rankText.textContent = '나의 석차: ' + itemCount + '명 중 ' + index + '등  (상위: ' + (index / itemCount * 100).toFixed(2) + '%)';
                }
                newHeading.appendChild(gradeText);
                newHeading.appendChild(rankText);

                parentElement.insertBefore(newHeading, parentElement.firstChild);

            })
                .catch(error => {
                // 오류 처리
                console.error('오류 발생:', error);
            });
        }
    }, 100);

    // 10초 후에 검색 종료
    setTimeout(function() {
        clearInterval(interval); // 검색 간격 멈추기
    }, 10000);
}

if (window.location.href.startsWith('https://kulms.korea.ac.kr/ultra/stream/telemetry/student/high-performance/')) {
    var fullURL = window.location.href;
    var lastNineCharacters = fullURL.slice(-9);
    viewMygradeFunc(lastNineCharacters);
}






function mediaDownloadFunc() {
    function downloadFile(url, filename, btn) {
        btn.style.pointerEvents = 'none'; // 클릭 이벤트 막기
        GM_xmlhttpRequest({
            method: "GET",
            url: url,
            headers: {
                "referer": "https://kucom.korea.ac.kr/",
            },
            responseType: "blob",
            onprogress: function (progress) {
                if (progress.lengthComputable) {
                    const percentComplete = (progress.loaded / progress.total) * 100;
                    btn.textContent = `${filename} 다운로드 중 (${percentComplete.toFixed(2)}%)`;
                } else {
                    btn.textContent = `${filename} 다운로드 중`;
                }
            },
            onload: function(response) {
                var blob = response.response;
                var link = document.createElement('a');
                link.href = window.URL.createObjectURL(blob);
                link.download = filename;
                link.click();
                btn.textContent = filename + ' 다운로드 완료';
            }
        });
    }

    const mediaExtensions = ['mp4', 'webm', 'ogg', 'ogv', 'avi', 'wmv', 'mkv', 'mov', 'flv', 'mp3', 'wav', 'ogg', 'm4a', 'aac', 'flac', 'opus', 'wma'];
    function waitForPlayTimeTextArea() {
        const playTimeTextArea = document.querySelector('.vc-pctrl-on-playing');
        if (playTimeTextArea) {
            let mediaElement = null;
            const audioElement = document.querySelector('#vc-sdplay-audio audio');
            const videoElement = document.querySelector('#video-play-video1 video');
            if (audioElement?.src?.includes('https') &&
                mediaExtensions.some(ext => audioElement.src.endsWith(`.${ext}`))
               ) {
                mediaElement = audioElement;
            } else if (videoElement?.src?.includes('https') &&
                       !videoElement?.src?.includes('intro.mp4') &&
                       !videoElement?.src?.includes('preloader.mp4') &&
                       mediaExtensions.some(ext => videoElement.src.endsWith(`.${ext}`))
                      ) {
                mediaElement = videoElement;
            }
            if (mediaElement) {
                const mediaSource = mediaElement.src;
                const downloadBtn = document.createElement('span');
                downloadBtn.classList.add('download', 'btn');
                downloadBtn.textContent = mediaElement.tagName === 'AUDIO' ? '오디오 다운로드' : '비디오 다운로드';
                downloadBtn.style.display = 'inline-block';
                downloadBtn.style.fontSize = '12px';
                downloadBtn.style.color = '#b1b1b1';
                downloadBtn.style.cursor = 'pointer';
                playTimeTextArea.insertAdjacentElement('afterend', downloadBtn);
                downloadBtn.addEventListener('mouseover', function() {
                    downloadBtn.style.color = '#ffffff';
                });

                downloadBtn.addEventListener('mouseout', function() {
                    downloadBtn.style.color = '#b1b1b1';
                });
                downloadBtn.addEventListener('click', function() {
                    const mediaSourceExtension = mediaSource.substr(mediaSource.lastIndexOf('.'));
                    const illegalCharsRegex = /[<>:"/\\|?*\x00-\x1F]/g; // Windows 파일 시스템에서 사용 불가능한 문자들 + null 문자
                    const cleanedFilename = content_title.replace(illegalCharsRegex, '').substring(0, 230);
                    const combinedFilename = cleanedFilename + mediaSourceExtension;
                    downloadFile(mediaSource, combinedFilename, downloadBtn);
                });
            } else {
                // audioElement나 videoElement가 없거나 src에 https가 포함되어 있지 않은 경우 100ms 후에 다시 시도합니다.
                setTimeout(waitForPlayTimeTextArea, 100);
            }
        } else {
            // .vc-pctrl-on-playing 요소가 존재하지 않는 경우 100ms 후에 다시 시도합니다.
            setTimeout(waitForPlayTimeTextArea, 100);
        }
    }
    waitForPlayTimeTextArea();
}

if (window.location.href.startsWith('https://kucom.korea.ac.kr/em/')) {
    mediaDownloadFunc();
}