Greasy Fork Thumbnail Adder

Adds thumbnails to the script list on Greasy Fork.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Greasy Fork Thumbnail Adder
// @name:ja      Greasy Fork Thumbnail Adder
// @namespace    http://tampermonkey.net/
// @version      0.6
// @description  Adds thumbnails to the script list on Greasy Fork.
// @description:ja Greasy Forkのスクリプト一覧に、詳細ページから抽出したサムネイルを追加表示します。
// @author       Gemini
// @match        https://greasyfork.org/*scripts*
// @license      MIT
// @grant        GM_xmlhttpRequest
// @connect      greasyfork.org
// @connect      *
// ==/UserScript==

(function() {
    'use strict';

    const scriptList = document.querySelectorAll('ol#browse-script-list > li');

    scriptList.forEach(li => {
        const article = li.querySelector('article');
        if (!article) return;

        const link = article.querySelector('h2 > a');
        if (!link) return;

        article.style.position = 'relative';
        article.style.paddingRight = '210px';
        article.style.minHeight = '150px';

        const scriptUrl = link.href;

        GM_xmlhttpRequest({
            method: "GET",
            url: scriptUrl,
            onload: function(response) {
                const parser = new DOMParser();
                const doc = parser.parseFromString(response.responseText, "text/html");

                // ページ内のすべての画像を取得して、条件に合うものを探すにゃ
                const allImgs = doc.querySelectorAll('#script-screenshots img, .step-content img, #additional-info img, article img');
                let validImgSrc = "";

                for (let img of allImgs) {
                    const isIcon = img.src.includes('avatar') || img.src.includes('logo') || img.src.includes('badge');

                    // 簡易的なサイズ判定
                    const isSmall = (img.width > 0 && img.width < 100) || (img.height > 0 && img.height < 100);

                    if (!isIcon && !isSmall) {
                        validImgSrc = img.src;
                        break; 
                    }
                }

                // 個別ページに画像がない場合、OGPを最終手段にするにゃ
                if (!validImgSrc) {
                    const ogImg = doc.querySelector('meta[property="og:image"]');
                    if (ogImg && !ogImg.content.includes('black-logo')) {
                        validImgSrc = ogImg.content;
                    }
                }

                if (validImgSrc) {
                    const thumbContainer = document.createElement('div');
                    thumbContainer.style.cssText = `
                        position: absolute;
                        top: 15px;
                        right: 15px;
                        width: 180px;
                        height: 135px;
                        display: flex;
                        align-items: center;
                        justify-content: center;
                        overflow: hidden;
                        background: #fff;
                        border: 1px solid #eee;
                        border-radius: 8px;
                        box-shadow: 0 1px 4px rgba(0,0,0,0.1); /* 影を小さく調整したにゃ */
                        z-index: 10;
                    `;

                    const img = document.createElement('img');
                    img.src = validImgSrc;
                    img.style.cssText = `
                        width: 100%;
                        height: 100%;
                        object-fit: contain;
                        background: #fafafa;
                    `;

                    thumbContainer.appendChild(img);
                    article.appendChild(thumbContainer);
                }
            }
        });
    });
})();