Greasy Fork

kone Gallery View

kone.gg 갤러리뷰

当前为 2025-06-05 提交的版本,查看 最新版本

// ==UserScript==
// @name         kone Gallery View
// @namespace    http://tampermonkey.net/
// @version      1.0.1
// @description  kone.gg 갤러리뷰
// @author       arcjay
// @match        https://kone.gg/s/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=kone.gg
// @grant        none
// @license      GPL
// ==/UserScript==
//'use strict';
//

/* 파라미터 */
const IS_STRICT_SQUARE_IMAGE_MODE = true; // true로 바꾸면 이미지사이즈가 300x300박스로 고정됨.
const REMOVE_SIDE_BAR_MODE = false; // true시, 우측의 인기글 사이드바가 사라짐.


/* 사이트의 셀렉터 변경될 경우 수정하기 */


/* VROW */
const SEL_VROWS_PARENT = 'main .grow .grow.flex-col .grow'
const ARTICLES_SELECTOR = ".grow .grow div.relative.flex.w-full"; // VROW (selectorAll로 쓰임)
const ARTICLES_WITHOUT_NOTICE_SELECTOR = ".grow .grow";
const VROW_SEL_RATE = ".col-span-1.text-xs.pt-0\\.5"
const VROW_SEL_TITLE = '.col-span-12 .overflow-hidden.text-nowrap.text-ellipsis';
const VROW_SEL_PREVIEW_IMAGE = 'img';

const ATTACH_TARGET_SELECTOR = ".col-span-12"; // 결과 이미지를 append할 위치

/* IMAGE 크롤러 */
const CONTENT_MAIN_SELECTOR = '.prose-container'


/* 수정할 일 없음 */
const IMAGE_WIDTH_SMALL = 150;
const IMAGE_WIDTH_NORMAL = 200;
const IMAGE_WIDTH_LARGE = 300;
const IMAGE_WIDTH = IMAGE_WIDTH_LARGE;

const SENSITIVE_WARN_IMG = false; // 민감성 포스팅 이미지를 비우선으로할지말지 표시유무
const FETCH_DELAY = 60; // 바꿀필요없음. 50이하로쓰면 봇감지될가능성높음0
const WIDTH_THRESHOLD = 1800; // 안쓰이는 변수 ( 적용할지말지고민하다 그냥 안씀 ) 당시의도 : px단위. 이 px보다 커야 이미지미리보기가 발생한다

/* 딜레이 */
const FIRST_DELAY = 1200;
const OBSERVER_DELAY = 1200

window.addEventListener("load", ()=>setTimeout(()=>{extendContentWrapper();observeUrlChange(start)}, FIRST_DELAY));

function vrowToGallery(vrowObj) {
    const {$self , $newRate, $rate, $title, $img} = vrowObj;

    $self.classList.remove('w-full');

    $self.classList.add('flex');
    $self.classList.add('flex-col');
    $self.classList.add('items-center');
    $self.classList.add('justify-center');
    $self.style.width='300px';
    $self.style.height='300px';
    $self.style.padding = '5px';

    $self.children[0].classList.remove('grow');
    $self.children[0].classList.remove('w-full');

    const $newTitle = document.createElement('div');
    $newTitle.style.overflow = 'hidden';
    $newTitle.style.whiteSpace = 'nowrap';
    $newTitle.style.textOverflow = 'ellipsis';
    $newTitle.style.width='300px';
    $newTitle.style.display = 'flex';
    $newTitle.style.justifyContent = 'center';

    $newTitle.append($title);

    $title.append($newRate);

    // toGallery()
    $img.classList.remove('max-w-50')
    $img.classList.remove('max-h-40')
    $img.style.width='280px';
    $img.style.height='250px';

    $self.replaceChildren($img,$newTitle,$rate)
}

async function start() {

    if (REMOVE_SIDE_BAR_MODE) removeRightSidebar();

    const vrows = document.querySelectorAll(ARTICLES_SELECTOR);
    const $parent = document.body.querySelector(SEL_VROWS_PARENT);

    //$parent.style.display = 'flex';
    //$parent.style={display:'flex',flexDirection:'row',flexWrap:'wrap'}

    $parent.classList.add('flex')
    $parent.classList.add('flex-wrap')
    $parent.classList.add('justify-center')

    console.log('parent', $parent, 'disp',$parent.style.display,$parent.classList)


    const vrowObjs = Array.from(vrows).map(vrow=>({$self:vrow,
                                                   $img:vrow.querySelector(VROW_SEL_PREVIEW_IMAGE) ?? document.createElement('img'),
                                                   $title:vrow.querySelector(VROW_SEL_TITLE),
                                                   $rate:vrow.querySelector(VROW_SEL_RATE),
                                                   $newRate : document.createElement('span'),
                                                   $IMG_APPEND_TARGET : vrow.querySelector(ATTACH_TARGET_SELECTOR)
                                                  }));

    vrowObjs.forEach(vrowToGallery);

    //const vrowsPromiseArray = Array.from(vrows).map((vrow, i) => imageFetchAndAttach(vrow, (i+1) * FETCH_DELAY));
    const articles = document.querySelectorAll(ARTICLES_WITHOUT_NOTICE_SELECTOR);
    articles.forEach((article) => {
        article.style = "height: auto !important";
    });

    //vrowObjs.forEach(attachImage);


    function attachImage(vrowObj){
        const {$img, $IMG_APPEND_TARGET} = vrowObj;
        //if ($img.width === 1000 && $img.height === 667) return; // 미방이미지는 attach하지않음
        $IMG_APPEND_TARGET.append($img);
    }

}



async function imageFetchAndAttach(vrow, delay) {
    // 이미지 페칭해서 붙임.
    const $img = vrow.querySelector('img');
    const $target = vrow.querySelector(ATTACH_TARGET_SELECTOR);
    
    attach($img)
    return;



    async function getImage(url){
        const response = await fetch(url);
        const text = await response.text();
        const html = new DOMParser().parseFromString(text, "text/html");

        const articleContent = html.querySelector(CONTENT_MAIN_SELECTOR);
        
        const tableImage = articleContent.querySelector('tbody img');
        const video = articleContent.querySelector('video');
        let image;

        if(tableImage) {
            image = tableImage;
        } else if (video && video.width >300) {
            image = new Image();
            image.src = video.poster;
        } else {
            const imageCandidates = articleContent.querySelectorAll('img');
            image = Array.from(imageCandidates).find(image=>SENSITIVE_WARN_IMG || !(image.width===1000 && image.height===667)) ?? new Image(300, 300);
        }
    }


    function attach(image)
    {
        fitImageSizeAndAlter(image);
        const target = vrow.querySelector(ATTACH_TARGET_SELECTOR);
        if (target) {
            target.style.width = `${IMAGE_WIDTH}px`;
            target.before(image);
        }

        vrow.style.display='flex';
        vrow.style.flexDirection='column';
        vrow.style.justifyContent = 'space-between';
        vrow.style.width=`${IMAGE_WIDTH}px`

    }
}


function fitImageSizeAndAlter(image) {
    const aspectRatio = image.height / image.width;
    image.style.display = 'block';
    image.style.width = `${IMAGE_WIDTH}px`;
    image.style.height = `${aspectRatio * IMAGE_WIDTH}px`;

    if(IS_STRICT_SQUARE_IMAGE_MODE){
        image.style.display = 'block';
        image.style.objectFit = 'contain';
        image.style.height = `${IMAGE_WIDTH}px`;
    }
}

function removeRightSidebar() {
    document.querySelector(".right-sidebar").style.display = "none";
}

function extendContentWrapper() {
    const contentWrapper = document.querySelector("main>.max-w-7xl");
    contentWrapper.classList.remove('max-w-7xl');
    contentWrapper.style.maxWidth = '95dvw'
    contentWrapper.children[0].classList.remove('md:container');

}


//
const observeUrlChange = (func) => {
    func();

    let oldHref = document.location.href;
    const body = document.querySelector('body');
    const observer = new MutationObserver(mutations => {
        if (oldHref !== document.location.href) {
            oldHref = document.location.href;
            setTimeout(func, OBSERVER_DELAY);

        }
    });
    observer.observe(body, { childList: true, subtree: true });
};