Greasy Fork

아카라이브 미리보기 이미지, 모두 열기

Extract image URL from specific element and log it to console

目前为 2024-04-15 提交的版本。查看 最新版本

// ==UserScript==
// @name         아카라이브 미리보기 이미지, 모두 열기
// @namespace    Violentmonkey Scripts
// @version      1.07
// @icon         https://www.google.com/s2/favicons?sz=64&domain=arca.live
// @description  Extract image URL from specific element and log it to console
// @author       You
// @match        https://arca.live/b/*
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// ==/UserScript==

// 설정 변수 false로 비활성화
var config = {
    openAllButton: true,      // 모두 열기 버튼 생성
    thumbnail: true,          // 미리보기 이미지 생성
    thumbWidth: 100,
    thumbHeight: 62,
    thumbnailHover: true,     // 미리보기 이미지 마우스 오버시 보이게
    thumbnailBlur: true,          // 블러 효과를 적용할지 여부
    blurAmount: '2px',        // 블러 효과의 정도
    originalThumbnail: true,  // 개념글 썸네일 클릭 시 원본 이미지 불러오기
    thumbnailHoverBest: true, // 개념글 미리보기 이미지 마우스 오버시 보이게
};

// 설정 창을 생성하는 함수
function createSettingsWindow() {
    // 기존 설정 창이 있으면 제거
    var existingSettingsWindow = document.getElementById('settingsWindow');
    if (existingSettingsWindow) {
        existingSettingsWindow.remove();
    }

    // 설정 창 요소 생성
    var settingsWindow = document.createElement('div');
    settingsWindow.id = 'settingsWindow';
    settingsWindow.style.position = 'fixed';
    settingsWindow.style.top = '50%';
    settingsWindow.style.left = '50%';
    settingsWindow.style.transform = 'translate(-50%, -50%)';
    settingsWindow.style.width = '200px'; // 너비 지정
    settingsWindow.style.padding = '20px';
    settingsWindow.style.background = '#ffffff';
    settingsWindow.style.border = '1px solid #cccccc';
    settingsWindow.style.borderRadius = '10px'; // 테두리 둥글기 설정
    settingsWindow.style.boxShadow = '0px 0px 10px rgba(0, 0, 0, 0.3)';
    settingsWindow.style.zIndex = '9999';
    settingsWindow.style.textAlign = 'left';
    settingsWindow.style.display = 'flex'; // flexbox 사용
    settingsWindow.style.flexDirection = 'column'; // 세로 방향으로 정렬
    settingsWindow.style.alignItems = 'center'; // 수직 가운데 정렬

    // 설정 창 제목 추가
    var settingsTitle = document.createElement('div');
    settingsTitle.innerHTML = 'Settings'; // 설정 창 제목
    settingsTitle.style.fontSize = '18px'; // 제목 폰트 크기
    settingsTitle.style.fontWeight = 'bold'; // 제목 폰트 굵기
    settingsTitle.style.marginBottom = '10px'; // 하단 간격 지정
    settingsWindow.appendChild(settingsTitle);

    // 설정 변수를 반복하여 설정 입력 요소를 생성
    for (var key in config) {
        if (config.hasOwnProperty(key)) {
            // 변수를 담을 div 요소 생성
            var configDiv = document.createElement('div');
            configDiv.style.marginBottom = '5px'; // 하단 간격 지정
            configDiv.style.display = 'flex'; // flexbox 사용
            configDiv.style.alignItems = 'center'; // 수직 가운데 정렬

            var label = document.createElement('label');
            label.innerHTML = key + ': ';
            label.style.marginRight = '5px'; // 오른쪽 마진 지정
            label.style.marginBottom = '3px'; // 하단 마진 지정

            // 레이블에 마우스를 올렸을 때 해당 변수에 대한 설명을 툴팁으로 표시
            label.addEventListener('mouseover', function() {
                var labelText = this.innerText.split(':')[0].trim(); // 레이블의 텍스트에서 변수 이름 추출
            });

            var input = document.createElement('input');
            input.type = (typeof config[key] === 'boolean') ? 'checkbox' : 'text';
            input.value = config[key];
            input.checked = config[key]; // 체크박스의 경우 checked 속성 사용
            if (input.type === 'text') {
                input.style.width = '40px'; // 입력 창의 너비를 40px로 설정
                input.style.height = '20px'; // 입력 창의 높이를 15px로 설정
                input.style.padding = '0 5px'; // 입력 창의 좌우 패딩을 5px로 설정
            }
            input.addEventListener('input', (function(key) {
                return function(event) {
                    if (key === 'blurAmount') {
                        event.target.value = event.target.value.replace(/\D/g, ''); // 숫자만 입력되도록
                    }
                    config[key] = event.target.type === 'checkbox' ? event.target.checked : event.target.value;
                };
            })(key));

            // div에 레이블과 인풋 추가
            configDiv.appendChild(label);
            configDiv.appendChild(input);

            // 설정 창에 div 추가
            settingsWindow.appendChild(configDiv);
        }
    }

    // 확인과 취소 버튼을 담을 div 요소 생성
    var buttonContainer = document.createElement('div');
    buttonContainer.style.display = 'flex'; // flexbox 사용
    buttonContainer.style.marginTop = '10px'; // 상단 간격 지정

    // 확인 버튼 추가
    var confirmButton = document.createElement('button');
    confirmButton.innerHTML = '확인';
    confirmButton.style.marginRight = '15px'; // 우측 마진 지정
    confirmButton.style.border = '1px solid #cccccc';
    confirmButton.style.borderRadius = '5px';
    confirmButton.addEventListener('click', function() {
        // 설정을 저장하고 설정 창을 닫습니다.
        saveConfig();
        settingsWindow.remove(); // 설정 창 제거
        location.reload(); // 페이지 새로고침
    });
    confirmButton.addEventListener('mouseover', function() {
        confirmButton.style.background = '#007bff'; // 마우스를 올렸을 때 배경색 변경
        confirmButton.style.color = '#ffffff'; // 글자색을 하얀색으로 변경
    });
    confirmButton.addEventListener('mouseout', function() {
        confirmButton.style.background = ''; // 마우스를 내렸을 때 배경색을 회색으로 복원
        confirmButton.style.color = '#000000'; // 글자색을 검정색으로 변경
    });
    buttonContainer.appendChild(confirmButton);

    // 취소 버튼 추가
    var cancelButton = document.createElement('button');
    cancelButton.innerHTML = '취소';
    cancelButton.style.border = '1px solid #cccccc';
    cancelButton.style.borderRadius = '5px';
    cancelButton.addEventListener('click', function() {
        // 설정 창만 닫습니다.
        settingsWindow.remove(); // 설정 창 제거
    });
    cancelButton.addEventListener('mouseover', function() {
        cancelButton.style.background = '#ff0000'; // 마우스를 올렸을 때 배경색 변경
        cancelButton.style.color = '#ffffff'; // 글자색을 하얀색으로 변경
    });
    cancelButton.addEventListener('mouseout', function() {
        cancelButton.style.background = ''; // 마우스를 내렸을 때 배경색을 회색으로 복원
        cancelButton.style.color = '#000000'; // 글자색을 검정색으로 변경
    });
    buttonContainer.appendChild(cancelButton);

    // 버튼 컨테이너를 설정 창에 추가
    settingsWindow.appendChild(buttonContainer);

    // 설정 창을 body에 추가
    document.body.appendChild(settingsWindow);
}

// 설정 값을 로컬 저장소에 저장
function saveConfig() {
    for (var key in config) {
        if (config.hasOwnProperty(key)) {
            GM_setValue(key, config[key]);
        }
    }
}

// 설정 값을 로컬 저장소에서 불러오기
function loadConfig() {
    for (var key in config) {
        if (config.hasOwnProperty(key)) {
            config[key] = GM_getValue(key, config[key]);
        }
    }
}

// 저장된 설정 값을 로드
loadConfig();

// 설정 버튼 생성
var settingsButtonCommand = GM_registerMenuCommand('설정', function() {
    createSettingsWindow();
});

// 모두 열기 버튼 생성
if (config.openAllButton) {
    var openAllButton = document.createElement('a');
    openAllButton.className = 'btn btn-sm btn-primary float-left';
    openAllButton.href = '#';
    openAllButton.innerHTML = '<span class="ion-android-list"></span><span> 모두 열기 </span>';

    openAllButton.addEventListener('click', function(event) {
        event.preventDefault();
        document.querySelectorAll('a.vrow.column:not(.notice)').forEach(function(element) {
            var href = element.getAttribute('href');
            var classes = element.className.split(' ');
            if (href && !classes.includes('filtered') && !classes.includes('filtered-keyword')) {
                window.open(href, '_blank');
            }
        });
    });

    var targetElement = document.querySelector('.form-control.select-list-type');
    targetElement.parentNode.insertBefore(openAllButton, targetElement);
}

// 페이지가 모두 로드된 후 실행
document.addEventListener('DOMContentLoaded', function() {
    // 미리보기 이미지 생성
    if (config.thumbnail) {
        document.querySelectorAll('a.vrow.column:not(.notice)').forEach(function(element) {
            var vcolId = element.querySelector('.vrow-top .vcol.col-id');
            var vcolTitle = element.querySelector('.vrow-top .vcol.col-title');
            vcolId.style.margin = '0';
            // vcolId.style.width = '3rem';

            var vcolThumb = document.createElement('span');
            vcolThumb.className = 'vcol col-thumb';
            vcolThumb.style.width = config.thumbWidth + 'px';
            vcolThumb.style.height = config.thumbHeight + 'px';

            vcolThumb.style.borderRadius = '3px'; // 50%로 설정하여 원 형태로 라운드 적용
            // vcolThumb.style.padding = '1px'; // 패딩 값은 적절한 값으로 설정하세요.
            // vcolThumb.style.border = '1px solid #bbb'; // 보더 스타일 및 두께, 색상은 적절한 값으로 설정하세요.
            vcolTitle.parentNode.insertBefore(vcolThumb, vcolTitle);

            var vrowPreview = element.querySelector('.vrow-preview');

            function createThumbnail(retryCount) {
                var imageElement = vrowPreview ? vrowPreview.querySelector('img') : null;

                if (!imageElement && retryCount < 3) {
                    // 이미지 요소가 없는 경우이고, 재시도 횟수가 3보다 작은 경우, 1초 후에 다시 함수를 호출하여 재시도
                    setTimeout(function() {
                        createThumbnail(retryCount + 1); // 재시도 횟수 증가하여 재호출
                    }, 100); // 0.1초 후에 다시 시도
                } else if (!imageElement) {
                    // 이미지 요소가 없는 경우이고, 재시도 횟수가 3인 경우에는 종료
                    console.log('Retry limit reached. Unable to find image element.');
                } else {
                    // 이미지 요소가 있는 경우에만 나머지 코드 실행
                    element.style.height = 'auto';
                    element.style.paddingTop = '3.75px';
                    element.style.paddingBottom = '3.75px';

                    var imageUrl = imageElement.getAttribute('src');

                    var image = document.createElement('img');
                    image.src = imageUrl;
                    image.style.width = '100%';
                    image.style.height = '100%';
                    image.style.objectFit = 'cover';

                    if (config.thumbnailBlur) {
                        image.style.filter = 'blur(' + config.blurAmount + ')'; // 흐림 효과 적용

                        // 마우스 이벤트 추가
                        image.addEventListener('mouseenter', function() {
                            image.style.filter = 'none'; // 마우스가 요소 위에 있을 때 블러 효과 제거
                        });

                        image.addEventListener('mouseleave', function() {
                            image.style.filter = 'blur(' + config.blurAmount + ')'; // 마우스가 요소를 벗어날 때 다시 블러 효과 적용
                        });
                    }

                    vcolThumb.appendChild(image);

                    vrowPreview.style.width = '30rem';
                    vrowPreview.style.height = 'auto';
                    vrowPreview.style.top = 'auto';
                    vrowPreview.style.left = '13.5rem';
                    vrowPreview.style.display = 'none';

                    if (config.thumbnailHover) {
                        image.addEventListener('mouseenter', function() {
                            imageElement.src = imageUrl.replace('&type=list', '');
                            vrowPreview.style.display = null;
                        });

                        image.addEventListener('mouseleave', function() {
                            vrowPreview.style.display = 'none';
                        });
                    }
                }
            }

            createThumbnail(0); // 함수 호출 및 재시도 횟수 초기값 설정
        });
    }
});


// 썸네일 클릭 시 원본 이미지 불러오기
if (config.originalThumbnail) {
    document.querySelectorAll('a.title.preview-image').forEach(function(link) {
        link.addEventListener('click', function(event) {
            event.preventDefault();  // 기본 동작 방지
            var imageUrl = link.querySelector('img').getAttribute('src').replace(/&type=list/g, '');
            window.location.href = imageUrl;
        });
    });
}

// 개념글 미리보기 이미지 마우스 오버시 보이게
if (config.thumbnailHoverBest) {
    // 이미지 요소 선택
    var imageElements = document.querySelectorAll('.vrow.hybrid .title.preview-image .vrow-preview img');

    // 각 이미지 요소에 이벤트 추가
    imageElements.forEach(function(imageElement) {
        // 이미지에 호버 이벤트 추가
        imageElement.addEventListener('mouseenter', function() {
            // 이미지의 부모 요소 찾기
            var parentDiv = imageElement.closest('.vrow.hybrid');

            // 이미지 URL에서 &type=list 부분 제거
            var imageUrl = imageElement.src.replace('&type=list', '');

            // 복제된 이미지 요소 생성
            var duplicateImageElement = document.createElement('img');
            duplicateImageElement.src = imageUrl;

            // 복제된 이미지의 스타일 설정
            duplicateImageElement.style.position = 'absolute';
            duplicateImageElement.style.width = '30rem';
            duplicateImageElement.style.height = 'auto';
            duplicateImageElement.style.top = 'auto';
            duplicateImageElement.style.left = '7.5rem'; // 오른쪽으로 10rem 이동
            duplicateImageElement.style.zIndex = '1';
            duplicateImageElement.style.padding = '5px';
            duplicateImageElement.style.border = '1px solid';
            duplicateImageElement.style.borderRadius = '5px';
            duplicateImageElement.style.boxSizing = 'content-box';
            duplicateImageElement.style.backgroundColor = '#fff'; // 배경색
            duplicateImageElement.style.borderColor = '#bbb'; // 테두리 색상

            // vrow hybrid 클래스에 align-items: center; 스타일 추가
            parentDiv.classList.add('hybrid');
            parentDiv.style.alignItems = 'center'; // 수직 가운데 정렬

            // 복제된 이미지 요소를 기존 이미지 요소 다음에 추가
            parentDiv.appendChild(duplicateImageElement);

            // 마우스를 이미지에서 떼었을 때 복제된 이미지 제거
            imageElement.addEventListener('mouseleave', function() {
                duplicateImageElement.remove();
            });
        });
    });
}