FastPic Upload

Загрузка изображений на FastPic с расширенными форматами

Versione datata 21/02/2025. Vedi la nuova versione l'ultima versione.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         FastPic Upload
// @name:en      FastPic Upload
// @namespace    http://tampermonkey.net/
// @version      5.3
// @description  Загрузка изображений на FastPic с расширенными форматами
// @description:en  Image uploading to FastPic with extended formats
// @author       С
// @license MIT
// @match        https://rutracker.org/forum/viewtopic.php?*
// @match        https://rutracker.org/forum/posting.php?*
// @match        https://nnmclub.to/forum/viewtopic.php?*
// @match        https://nnmclub.to/forum/posting.php?*
// @match        https://tapochek.net/viewtopic.php?*
// @match        https://tapochek.net/posting.php*
// @match        https://4pda.to/forum/index.php*
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// ==/UserScript==

(function() {
    'use strict';

    // Настройки по умолчанию
    const DEFAULT_SETTINGS = {
        uploadService: 'fastpic',
        fastpic: {
            codeFormat: 'bb_thumb',  // direct, bb_thumb, bb_full, html_thumb, markdown_thumb
            thumb: {
                checkThumb: 'size',  // 'size', 'text', 'filename', 'no'
                thumbText: 'Увеличить',
                thumbSize: '150',
                thumbSizeVertical: false
            },
            image: {
                origResize: {
                    enabled: false,
                    resSelect: '500',  // '150', '320', '500', '640', '800'
                    customSize: '500'
                },
                origRotate: {
                    enabled: false,
                    value: '0'  // '0', '90', '180', '270'
                },
                optimization: {
                    enabled: false,
                    jpegQuality: '75'  // 0-99
                },
                poster: false
            }
        },
        imgbb: {
            codeFormat: 'bb_thumb_linked',  // viewer_link, direct, html_image, html_full_linked, html_medium_linked, html_thumb_linked, bb_full, bb_full_linked, bb_medium_linked, bb_thumb_linked
            apiKey: '',
            expiration: '',  //значение в секундах
            useOriginalFilename: false
        },
        imagebam: {
            codeFormat: 'bb_thumb',  // direct, bb_thumb, html_thumb
            thumbnailSize: '2',  // размер превью: 1, 2, 3, 4
            contentType: 'sfw',  // тип контента: nsfw, sfw
            galleryEnabled: false,  // включить галерею
            galleryTitle: '' // название галереи
        }
    };

    // Утилитарные функции стилизации
    function addScriptStyles() {
        const style = document.createElement('style');
        style.textContent = `
            .fastpic-upload-progress {
                position: fixed;
                top: 20px;
                right: 20px;
                background: #fff;
                padding: 10px;
                border: 1px solid #ccc;
                border-radius: 5px;
                box-shadow: 0 2px 5px rgba(0,0,0,0.2);
                z-index: 9999;
            }
        `;
        document.head.appendChild(style);
    }

    // Функция для определения текущего сайта
    function getCurrentSite() {
        const hostname = window.location.hostname;
        if (hostname.includes('rutracker')) return 'rutracker';
        if (hostname.includes('nnmclub')) return 'nnmclub';
        if (hostname.includes('tapochek')) return 'tapochek';
        if (hostname.includes('4pda')) return '4pda';
        return null;
    }

    // Функция для поиска textarea
    function findTextarea() {
        const site = getCurrentSite();

        switch(site) {
            case 'rutracker':
                return document.querySelector('#post-textarea');
            case 'tapochek':
                return document.querySelector('textarea.editor[name="message"]');
            case 'nnmclub':
                return document.querySelector('#post_body');
            case '4pda':
                return document.querySelector('#ed-0_textarea') || document.querySelector('.ed-textarea');
            default:
                return null;
        }
    }

    // Функция для показа уведомлений
    function showNotification(message, duration = 3000, sessionUrl = '') {
        const notification = document.createElement('div');
        notification.className = 'fastpic-notification';
        notification.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: #fff;
            padding: 10px 20px;
            border-radius: 5px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
            z-index: 9999;
        `;

        if (sessionUrl) {
            const link = document.createElement('a');
            link.href = sessionUrl;
            link.target = '_blank';
            link.textContent = message;
            notification.appendChild(link);
        } else {
            notification.textContent = message;
        }

        document.body.appendChild(notification);

        setTimeout(() => notification.remove(), duration);
    }

    // Функция форматирования кода
    function formatCode(image) {
        const serviceSettings = settings[settings.uploadService];

        if (settings.uploadService === 'fastpic') {

            // Форматы FastPic
            const formats = {
                direct: image.imagePath,
                bb_thumb: `[URL=${image.viewUrl}][IMG]${image.thumbPath}[/IMG][/URL]`,
                bb_full: `[URL=${image.viewUrl}][IMG]${image.imagePath}[/IMG][/URL]`,
                html_thumb: `<a href="${image.viewUrl}" target="_blank"><img src="${image.thumbPath}" border="0"></a>`,
                markdown_thumb: `[![FastPic.Ru](${image.thumbPath})](${image.viewUrl})`
            };

            return formats[serviceSettings.codeFormat]
                .replace('{viewUrl}', image.viewUrl)
                .replace('{imagePath}', image.imagePath)
                .replace('{thumbPath}', image.thumbPath);

            // Форматы ImgBB
        } else if (settings.uploadService === 'imgbb') {
            const formats = {
                viewer_link: image.url_viewer,  // Ссылка на просмотр
                direct: image.url,  // Прямая ссылка

                html_image: `<img src="${image.url}" alt="${image.name}" border="0">`,  // HTML-код изображения
                html_full_linked: `<a href="${image.url_viewer}"><img src="${image.url}" alt="${image.name}" border="0"></a>`,  // HTML-код полноразмерного со ссылкой
                html_medium_linked: `<a href="${image.url_viewer}"><img src="${image.medium?.url || image.url}" alt="${image.name}" border="0"></a>`,  // HTML-код среднего размера со ссылкой
                html_thumb_linked: `<a href="${image.url_viewer}"><img src="${image.thumb.url}" alt="${image.name}" border="0"></a>`,  // HTML-код миниатюры со ссылкой

                bb_full: `[img]${image.url}[/img]`,  // BB-код полноразмерного
                bb_full_linked: `[url=${image.url_viewer}][img]${image.url}[/img][/url]`,  // BB-код полноразмерного со ссылкой
                bb_medium_linked: `[url=${image.url_viewer}][img]${image.medium?.url || image.url}[/img][/url]`,  // BB-код среднего размера со ссылкой
                bb_thumb_linked: `[url=${image.url_viewer}][img]${image.thumb.url}[/img][/url]`  // BB-код миниатюры со ссылкой
            };

            return formats[serviceSettings.codeFormat]
                .replace('{viewUrl}', image.url_viewer)
                .replace('{imagePath}', image.url)
                .replace('{thumbPath}', image.thumb.url)
                .replace('{mediumPath}', image.medium?.url || image.url);

            // Форматы ImageBam
        } else if (settings.uploadService === 'imagebam') {
            const formats = {
                direct: image.url_viewer,  // Прямая ссылка - ссылка на просмотр
                bb_thumb: `[URL=${image.url_viewer}][IMG]${image.thumb.url}[/IMG][/URL]`,  // BB-код с превью
                html_thumb: `<a href="${image.url_viewer}" target="_blank"><img src="${image.thumb.url}" alt=""/></a>`  // HTML-код
            };
            return formats[serviceSettings.codeFormat]
                .replace('{viewUrl}', image.url_viewer)
                .replace('{imagePath}', image.thumb.url.replace('_t.', '.'))
                .replace('{thumbPath}', image.thumb.url);
        }
    }

    // Функция для парсинга ответа FastPic
    function parseFastPicResponse(responseText) {
        // Ищем все блоки UploadSettings в ответе
        const uploadSettingsRegex = /<UploadSettings[^>]*>([\s\S]*?)<\/UploadSettings>/g;
        const results = [];
        let match;

        while ((match = uploadSettingsRegex.exec(responseText)) !== null) {
            const settingsXml = match[0];

            // Извлекаем нужные значения из каждого блока
            const status = settingsXml.match(/<status>([^<]+)<\/status>/)?.[1];

            if (status === 'ok') {
                const imagePath = settingsXml.match(/<imagepath>([^<]+)<\/imagepath>/)?.[1];
                const thumbPath = settingsXml.match(/<thumbpath>([^<]+)<\/thumbpath>/)?.[1];
                const viewUrl = settingsXml.match(/<viewurl>([^<]+)<\/viewurl>/)?.[1];
                const sessionUrl = settingsXml.match(/<sessionurl>([^<]+)<\/sessionurl>/)?.[1];

                if (imagePath && thumbPath && viewUrl) {
                    results.push({
                        imagePath,
                        thumbPath,
                        viewUrl,
                        sessionUrl
                    });
                }
            } else {
                const error = settingsXml.match(/<error>([^<]+)<\/error>/)?.[1] || 'Неизвестная ошибка';
                throw new Error(error);
            }
        }

        // Извлекаем URL сессии из XML ответа FastPic
        const sessionUrl = responseText.match(/<viewurl>([^<]+)<\/viewurl>/)?.[1] || '';

        return { results, sessionUrl };
    }

    // Функция для загрузки на FastPic
    async function uploadToFastPic(files) {
        const formData = new FormData();
        for (let i = 0; i < files.length; i++) {
            formData.append(`file${i + 1}`, files[i]);
        }

        // Добавляем параметры FastPic
        formData.append('uploading', files.length.toString());
        formData.append('check_thumb', settings.fastpic.thumb.checkThumb);
        formData.append('thumb_text', settings.fastpic.thumb.thumbText);
        formData.append('thumb_size', settings.fastpic.thumb.thumbSize);

        if (settings.fastpic.thumb.thumbSizeVertical) {
            formData.append('check_thumb_size_vertical', '1');
        }

        if (settings.fastpic.image.origResize.enabled) {
            formData.append('check_orig_resize', '1');
            formData.append('res_select', settings.fastpic.image.origResize.resSelect);
            formData.append('orig_resize', settings.fastpic.image.origResize.customSize);
        } else {
            // Явно отключаем изменение размера
            formData.append('check_orig_resize', '0');
            formData.append('res_select', '0');
            formData.append('orig_resize', '0');
        }

        if (settings.fastpic.image.origRotate.enabled) {
            formData.append('check_orig_rotate', '1');
            formData.append('orig_rotate', settings.fastpic.image.origRotate.value);
        }

        if (settings.fastpic.image.optimization.enabled) {
            formData.append('check_optimization', 'on');
            formData.append('jpeg_quality', settings.fastpic.image.optimization.jpegQuality);
        }

        if (settings.fastpic.image.poster) {
            formData.append('check_poster', 'on');
        }

        formData.append('submit', 'Загрузить');

        const response = await new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: 'https://fastpic.org/upload?api=1',
                data: formData,
                onload: (response) => resolve(response),
                onerror: (error) => reject(error)
            });
        });

        return parseFastPicResponse(response.responseText);
    }

    // Функция для загрузки на ImgBB
    async function uploadToImgBB(file) {
        if (!settings.imgbb.apiKey) {
            throw new Error(`Требуется API ключ ImgBB`);
        }

        const base64 = await new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onload = () => resolve(reader.result.split(',')[1]);
            reader.onerror = reject;
            reader.readAsDataURL(file);
        });

        const formData = new FormData();
        formData.append('image', base64);

        // Передаем имя файла только если включена опция
        if (settings.imgbb.useOriginalFilename) {
            formData.append('name', file.name);
        }

        // Добавляем параметр только если выбран срок хранения
        if (settings.imgbb.expiration) {
            formData.append('expiration', settings.imgbb.expiration);
        }

        const response = await new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: `https://api.imgbb.com/1/upload?key=${settings.imgbb.apiKey}`,
                data: formData,
                onload: (response) => resolve(response),
                onerror: (error) => reject(error)
            });
        });

        const data = JSON.parse(response.responseText);
        if (!data.success) {
            throw new Error(data.error?.message || 'Ошибка загрузки на ImgBB');
        }

        return data.data;
    }

    // Функция для загрузки на ImageBam
    async function uploadToImageBam(file) {
        // Получаем XSRF токен
        const sessionResponse = await new Promise(resolve => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: 'https://www.imagebam.com',
                headers: { 'Accept': 'text/html' },
                onload: resolve
            });
        });

        const xsrfToken = sessionResponse.responseHeaders.match(/XSRF-TOKEN=([^;]+)/)?.[1];
        if (!xsrfToken) throw new Error('Не удалось получить токен');

        // Формируем данные для создания сессии
        let data = `thumbnail_size=${settings.imagebam.thumbnailSize}&content_type=${settings.imagebam.contentType}&comments_enabled=false`;
        if (settings.imagebam.galleryEnabled && settings.imagebam.galleryTitle) {
            data += `&gallery=true&gallery_title=${encodeURIComponent(settings.imagebam.galleryTitle)}`;
        }

        // Создаем сессию
        const uploadSessionResponse = await new Promise(resolve => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: 'https://www.imagebam.com/upload/session',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                    'X-XSRF-TOKEN': decodeURIComponent(xsrfToken),
                    'Cookie': `XSRF-TOKEN=${xsrfToken}`,
                    'Accept': 'application/json',
                    'Origin': 'https://www.imagebam.com',
                    'Referer': 'https://www.imagebam.com/upload'
                },
                data: data,
                onload: resolve
            });
        });

        const sessionData = JSON.parse(uploadSessionResponse.responseText);
        if (!sessionData.session) {
            throw new Error('Ошибка создания сессии: отсутствует параметр session');
        }

        const sessionId = sessionData.session;

        // Загружаем файл
        const formData = new FormData();
        formData.append('data', sessionData.data);
        formData.append('files[0]', file);

        const uploadResponse = await new Promise(resolve => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: `https://www.imagebam.com/upload?session=${sessionId}`,
                headers: {
                    'X-XSRF-TOKEN': decodeURIComponent(xsrfToken),
                    'Cookie': `XSRF-TOKEN=${xsrfToken}`,
                    'Origin': 'https://www.imagebam.com',
                    'Referer': 'https://www.imagebam.com/upload'
                },
                data: formData,
                onload: resolve
            });
        });

        const uploadResult = JSON.parse(uploadResponse.responseText);
        if (!uploadResult.success) throw new Error('Ошибка загрузки');

        // Получаем BB-код
        const completeResponse = await new Promise(resolve => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: uploadResult.success,
                headers: {
                    'X-XSRF-TOKEN': decodeURIComponent(xsrfToken),
                    'Cookie': `XSRF-TOKEN=${xsrfToken}`,
                    'Referer': 'https://www.imagebam.com/upload'
                },
                onload: resolve
            });
        });

        // Ищем BB-код в input'ах
        const doc = new DOMParser().parseFromString(completeResponse.responseText, 'text/html');
        const bbcode = Array.from(doc.querySelectorAll('input[type="text"]'))
            .map(input => input.value)
            .find(value => value.includes('[URL=') && value.includes('_t.'));

        if (!bbcode) throw new Error('Не удалось найти BB-код');

        // Извлекаем URL
        const urlMatch = bbcode.match(/\[URL=([^\]]+)\]/);
        const imgMatch = bbcode.match(/\[IMG\]([^\]]+)\[\/IMG\]/);

        if (!urlMatch || !imgMatch) throw new Error('Неправильный формат BB-кода');

        return {
            url: imgMatch[1].replace('_t.', '.'),
            url_viewer: urlMatch[1],
            thumb: { url: imgMatch[1] },
            session: sessionId
        };
    }

    // Функция для загрузки изображений
    async function uploadImages(files) {
        switch (settings.uploadService) {
            case 'fastpic':
                return await uploadToFastPic(files);

            case 'imgbb':
                const imgbbResults = [];
                for (const file of files) {
                    const result = await uploadToImgBB(file);
                    imgbbResults.push(result);
                }
                return { results: imgbbResults };

            case 'imagebam':
                const imagebamResults = [];
                for (const file of files) {
                    const result = await uploadToImageBam(file);
                    imagebamResults.push(result);
                }
                return { results: imagebamResults };

            default:
                throw new Error('Неподдерживаемый сервис загрузки');
        }
    }

    // Общая функция обработки загрузки изображений
    async function handleImageUpload(files) {
        if (files.length === 0) return;

        // Проверка форматов и размера
        const allowedFormats = ['image/gif', 'image/jpeg', 'image/png', 'image/webp', 'image/bmp'];
        const maxFileSizes = {
            'fastpic': 25 * 1024 * 1024,  // 25MB
            'imgbb': 32 * 1024 * 1024,    // 32MB
        };
        const maxFiles = {
            'fastpic': 30,
            'imgbb': 30,
        };

        const maxFileSize = maxFileSizes[settings.uploadService];
        const maxFileCount = maxFiles[settings.uploadService];

        // Фильтруем файлы по формату и размеру
        const validFiles = Array.from(files).filter(file => {
            if (!allowedFormats.includes(file.type)) {
                showNotification(`Файл ${file.name} имеет неподдерживаемый формат. Разрешены: gif, jpeg, png, webp, bmp`);
                return false;
            }
            if (file.size > maxFileSize) {
                showNotification(`Файл ${file.name} превышает максимальный размер в ${Math.floor(maxFileSize / 1024 / 1024)}MB`);
                return false;
            }
            return true;
        });

        if (validFiles.length === 0) {
            showNotification('Нет подходящих файлов для загрузки');
            return;
        }

        if (validFiles.length > maxFileCount) {
            showNotification(`Можно загрузить максимум ${maxFileCount} файлов одновременно`);
            return false;
        }

        // Создаем индикатор прогресса
        const progressDiv = document.createElement('div');
        progressDiv.className = 'fastpic-upload-progress';
        document.body.appendChild(progressDiv);

        const textarea = findTextarea();
        if (!textarea) return;

        // Сохраняем позицию курсора
        const cursorPos = textarea.selectionStart;
        let formattedCode = '';

        try {
            progressDiv.textContent = `Загрузка ${validFiles.length} изображений...`;

            const { results: images } = await uploadImages(validFiles);

            // Берем sessionUrl из первого изображения если это FastPic и ImageBam
            let sessionUrl = null;
            if (settings.uploadService === 'fastpic') {
                sessionUrl = images[0]?.sessionUrl;

            // Для ImageBam формируем ссылку на сессию
            } else if (settings.uploadService === 'imagebam') {
                sessionUrl = `https://www.imagebam.com/upload/complete?session=${images[0]?.session}`;
            }

            // Формируем код для всех изображений
            let formattedCode = images.map(image => formatCode(image)).join(' ');

            // Вставляем formattedCode в текстовое поле
            const textBefore = textarea.value.substring(0, cursorPos);
            const textAfter = textarea.value.substring(cursorPos);
            textarea.value = textBefore + formattedCode + textAfter;
            textarea.selectionStart = textarea.selectionEnd = cursorPos + formattedCode.length;

            showNotification(`Успешно загружено ${images.length} изображений`, 3000, sessionUrl);
        } catch (error) {
            console.error('Ошибка при загрузке изображений:', error);
            showNotification(`Ошибка при загрузке изображений: ${error.message}`, 5000);
        } finally {
            setTimeout(() => progressDiv.remove(), 3000);
        }
    }

    // Создание меню настроек
    function createSettingsMenu() {
        const site = getCurrentSite();
        if (!site) return;

        const menuButton = document.createElement('input');
        menuButton.type = 'button';
        menuButton.value = 'Настройки FastPic Upload';
        menuButton.id = 'fastpic-settings-btn';
        menuButton.style.cssText = 'margin: 0 5px;';

        // стиль
        if (site === '4pda') {
            menuButton.className = 'zbtn zbtn-default';
        }

        // Добавление кнопки в зависимости от сайта
        switch(site) {
            case 'rutracker':
                const rutrackerNav = document.querySelector('#ped-submit-buttons');
                if (rutrackerNav) {
                    rutrackerNav.appendChild(menuButton);
                }
                break;

            case 'tapochek':
                const tapochekNav = document.querySelector('.mrg_4.tCenter');
                if (tapochekNav) {
                    tapochekNav.appendChild(menuButton);
                }
                break;

            case 'nnmclub':
                const nnmNav = document.querySelector('td.row2[align="center"][valign="middle"][style*="padding: 6px"]');
                if (nnmNav) {
                    nnmNav.appendChild(menuButton);
                }
                break;

            case '4pda':
                const pdaNav = document.querySelector('.dfrms.text-center');
                if (pdaNav) {
                    pdaNav.appendChild(menuButton);
                }
                break;
        }

        menuButton.addEventListener('click', showSettingsDialog);
    }

    // Создание диалога настроек
    function showSettingsDialog(e) {
        e.preventDefault();

        const dialog = document.createElement('div');
        dialog.className = 'fastpic-settings-dialog';
        dialog.innerHTML = `
            <style>
                .fastpic-settings-dialog {
                    position: fixed;
                    top: 50%;
                    left: 50%;
                    transform: translate(-50%, -50%);
                    background: #fff;
                    padding: 20px;
                    border-radius: 8px;
                    box-shadow: 0 2px 10px rgba(0,0,0,0.3);
                    z-index: 10000;
                    min-width: 300px;
                    max-height: 80vh;
                    overflow-y: auto;
                }
                .fastpic-settings-dialog h3 {
                    margin-top: 0;
                    margin-bottom: 15px;
                }
                .fastpic-settings-dialog .setting-group {
                    margin-bottom: 15px;
                    padding: 10px;
                    border: 1px solid #ddd;
                    border-radius: 4px;
                }
                .fastpic-settings-dialog label {
                    display: block;
                    margin-bottom: 5px;
                }
                .fastpic-settings-dialog select,
                .fastpic-settings-dialog input[type="text"] {
                    width: 100%;
                    padding: 5px;
                    margin-bottom: 10px;
                }
                .fastpic-settings-dialog select[id="fastpic-checkThumb"]:not([value="text"]) ~ #fastpic-thumbText-container {
                    display: none;
                }
                .fastpic-settings-dialog .buttons {
                    text-align: right;
                    margin-top: 15px;
                    display: flex;
                    justify-content: space-between;
                }
                .fastpic-settings-dialog .right-buttons {
                    display: flex;
                    gap: 10px;
                }
                .fastpic-settings-dialog button.reset {
                    background-color: #fff;
                    color: black;
                    border: none;
                    padding: 5px 5px;
                    border-radius: 4px;
                    cursor: pointer;
                }
                .fastpic-settings-dialog button.reset:hover {
                    background-color: #fff;
                }
                .fastpic-overlay {
                    position: fixed;
                    top: 0;
                    left: 0;
                    right: 0;
                    bottom: 0;
                    background: rgba(0,0,0,0.5);
                    z-index: 9999;
                }
                .service-settings {
                    display: none;
                }
                .service-settings.active {
                    display: block;
                }
            </style>
            <h3>Настройки FastPic Upload</h3>
            <div class="setting-group">
                <label>Сервис загрузки:</label>
                <select id="uploadService">
                    <option value="fastpic">FastPic</option>
                    <option value="imgbb">ImgBB</option>
                    <option value="imagebam">ImageBam</option>
                </select>
            </div>

            <!-- Настройки FastPic -->
            <div id="fastpic-settings" class="service-settings setting-group">
                <h4>Настройки FastPic</h4>

                <!-- Existing code format setting -->
                <label>Формат кода:</label>
                <select id="fastpic-codeFormat">
                    <option value="direct">Прямая ссылка</option>
                    <option value="bb_thumb">BB-код (превью)</option>
                    <option value="bb_full">BB-код (большое изображение)</option>
                    <option value="html_thumb">HTML</option>
                    <option value="markdown_thumb">Markdown</option>
                </select>
                <!-- Preview settings -->
                <div class="setting-group">
                    <h5>Настройки превью</h5>
                    <label>Надпись в превью:</label>
                    <select id="fastpic-checkThumb">
                        <option value="size">Размер</option>
                        <option value="text">Текст</option>
                        <option value="filename">Имя файла</option>
                        <option value="no">Без надписи</option>
                    </select>
                    <div id="fastpic-thumbText-container">
                        <label>Текст превью:</label>
                        <input type="text" id="fastpic-thumbText" value="Увеличить">
                    </div>
                    <label>Размер превью (px):</label>
                    <input type="number" id="fastpic-thumbSize" min="1" max="999" value="150">
                    <label>
                        <input type="checkbox" id="fastpic-thumbSizeVertical">
                        по высоте
                    </label>
                </div>

                <!-- Image settings -->
                <div class="setting-group">
                    <h5>Настройки изображения</h5>

                    <!-- Resize settings -->
                    <label>
                        <input type="checkbox" id="fastpic-origResizeEnabled">
                        Уменьшить
                    </label>
                    <div id="fastpic-resizeOptions" style="margin-left: 20px;">
                        <label>Предустановленный размер:</label>
                        <select id="fastpic-resSelect">
                            <option value="150">150px</option>
                            <option value="320">320px</option>
                            <option value="500">500px</option>
                            <option value="640">640px</option>
                            <option value="800">800px</option>
                        </select>
                        <label>Размер по вертикали (px):</label>
                        <input type="number" id="fastpic-customSize" value="500">
                    </div>

                    <!-- Rotation settings -->
                    <label>
                        <input type="checkbox" id="fastpic-origRotateEnabled">
                        Повернуть
                    </label>
                    <div id="fastpic-rotateOptions" style="margin-left: 20px;">
                        <select id="fastpic-origRotate">
                            <option value="0">0°</option>
                            <option value="90">90° по часовой</option>
                            <option value="180">180°</option>
                            <option value="270">90° против часовой</option>
                        </select>
                    </div>

                    <!-- Optimization settings -->
                    <label>
                        <input type="checkbox" id="fastpic-optimizationEnabled">
                        Оптимизировать в JPEG
                    </label>
                    <div id="fastpic-optimizationOptions" style="margin-left: 20px;">
                        <label>Качество JPEG (0-99):</label>
                        <input type="number" id="fastpic-jpegQuality" min="0" max="99" value="85">
                    </div>

                    <!-- Poster setting -->
                    <label>
                        <input type="checkbox" id="fastpic-poster">
                        Постер
                    </label>
                </div>
            </div>

            <!-- Настройки ImgBB -->
            <div id="imgbb-settings" class="service-settings setting-group">
                <h4>Настройки ImgBB</h4>
                <label><a href="https://api.imgbb.com/" target="_blank">API ключ ImgBB</a>:</label>
                <input type="text" id="imgbb-apiKey" placeholder="Введите API ключ ImgBB">
                <label>Формат кода:</label>
                <select id="imgbb-codeFormat">
                    <option value="viewer_link">Ссылка на просмотр</option>
                    <option value="direct">Прямая ссылка</option>
                    <option value="html_image">HTML-код изображения</option>
                    <option value="html_full_linked">HTML-код полноразмерного со ссылкой</option>
                    <option value="html_medium_linked">HTML-код среднего размера со ссылкой</option>
                    <option value="html_thumb_linked">HTML-код миниатюры со ссылкой</option>
                    <option value="bb_full">BB-код полноразмерного</option>
                    <option value="bb_full_linked">BB-код полноразмерного со ссылкой</option>
                    <option value="bb_medium_linked">BB-код среднего размера со ссылкой</option>
                    <option value="bb_thumb_linked">BB-код миниатюры со ссылкой</option>
                </select>
                <label>Срок хранения:</label>
                <select id="imgbb-expiration">
                    <option value="" selected="">Никогда не удалять</option>
                    <option value="60">1 минута</option>
                    <option value="300">5 минут</option>
                    <option value="900">15 минут</option>
                    <option value="1800">30 минут</option>
                    <option value="3600">1 час</option>
                    <option value="10800">3 часа</option>
                    <option value="21600">6 часов</option>
                    <option value="43200">12 часов</option>
                    <option value="86400">1 день</option>
                    <option value="172800">2 дня</option>
                    <option value="259200">3 дня</option>
                    <option value="345600">4 дня</option>
                    <option value="432000">5 дней</option>
                    <option value="518400">6 дней</option>
                    <option value="604800">1 неделя</option>
                    <option value="1209600">2 недели</option>
                    <option value="1814400">3 недели</option>
                    <option value="2592000">1 месяц</option>
                    <option value="5184000">2 месяца</option>
                    <option value="7776000">3 месяца</option>
                    <option value="10368000">4 месяца</option>
                    <option value="12960000">5 месяцев</option>
                    <option value="15552000">6 месяцев</option>
                </select>
                <label>
                    <input type="checkbox" id="imgbb-useOriginalFilename"> Оригинальное имя файла
                </label>
            </div>

            <!-- Настройки ImageBam -->
            <div id="imagebam-settings" class="service-settings setting-group">
                <h4>Настройки ImageBam</h4>
                <label>Формат кода:</label>
                <select id="imagebam-codeFormat">
                    <option value="direct">Прямая ссылка</option>
                    <option value="bb_thumb">BB-код (превью)</option>
                    <option value="html_thumb">HTML-код</option>
                </select>
                <label>Размер превью:</label>
                <select id="imagebam-thumbnailSize">
                    <option value="1">100x100 (small)</option>
                    <option value="2">180x180 (standard)</option>
                    <option value="3">250x250 (large)</option>
                    <option value="4">300x300 (extra large)</option>
                </select>
                <label>Тип контента:</label>
                <select id="imagebam-contentType">
                    <option value="sfw">Family Safe Content</option>
                    <option value="nsfw">Adult Content</option>
                </select>
                <div>
                    <label>
                        <input type="checkbox" id="imagebam-galleryEnabled"> Использовать галерею
                    </label>
                </div>
                <div id="imagebam-gallery-options" style="display:none; margin-left: 20px; margin-top: 10px;">
                    <label>Название галереи:</label>
                    <input type="text" id="imagebam-galleryTitle" placeholder="Введите название галереи">
                </div>
            </div>

            <div class="buttons">
                <button class="reset" id="resetSettings">Сброс</button>
                <div class="right-buttons">
                    <button id="cancelSettings">Отмена</button>
                    <button id="saveSettings">Сохранить</button>
                </div>
            </div>
        `;

        const overlay = document.createElement('div');
        overlay.className = 'fastpic-overlay';
        document.body.appendChild(overlay);
        document.body.appendChild(dialog);

        overlay.addEventListener('click', (e) => {
            // Проверяем, что клик был именно по оверлею, а не по диалогу
            if (e.target === overlay) {
                dialog.remove();
                overlay.remove();
            }
        });

        // Предотвращение закрытия при клике по самому диалогу
        dialog.addEventListener('click', (e) => {
            e.stopPropagation();
        });

        // Заполняем текущими настройками
        dialog.querySelector('#uploadService').value = settings.uploadService;

        // Обработчик кнопки сброса
        dialog.querySelector('#resetSettings').addEventListener('click', () => {
            // Сохраняем API ключ
            const apiKey = settings.imgbb.apiKey;

            // Сохраняем текущий выбранный сервис
            const currentService = dialog.querySelector('#uploadService').value;

            // Сбрасываем настройки к значениям по умолчанию
            settings = JSON.parse(JSON.stringify(DEFAULT_SETTINGS));

            // Восстанавливаем API ключ
            settings.imgbb.apiKey = apiKey;

            // Восстанавливаем текущий сервис
            settings.uploadService = currentService;

            // Сохраняем обновленные настройки
            saveSettings();

            // Перезагружаем диалог с сохранением текущего сервиса
            dialog.remove();
            overlay.remove();
            showSettingsDialog(new Event('click'));

            // Показываем уведомление
            showNotification('Настройки сброшены до значений по умолчанию');
        });

        // FastPic настройки
        const fastpicSettings = settings.fastpic;
        // codeFormat
        dialog.querySelector('#fastpic-codeFormat').value = fastpicSettings.codeFormat;

        // Настройки thumb
        dialog.querySelector('#fastpic-checkThumb').value = fastpicSettings.thumb.checkThumb;
        dialog.querySelector('#fastpic-thumbText').value = fastpicSettings.thumb.thumbText;
        dialog.querySelector('#fastpic-thumbSize').value = fastpicSettings.thumb.thumbSize;
        dialog.querySelector('#fastpic-thumbSizeVertical').checked = fastpicSettings.thumb.thumbSizeVertical;

        // Настройки image.origResize
        dialog.querySelector('#fastpic-origResizeEnabled').checked = fastpicSettings.image.origResize.enabled;
        dialog.querySelector('#fastpic-resSelect').value = fastpicSettings.image.origResize.resSelect;
        dialog.querySelector('#fastpic-customSize').value = fastpicSettings.image.origResize.customSize;

        // Настройки origRotate
        dialog.querySelector('#fastpic-origRotateEnabled').checked = fastpicSettings.image.origRotate.enabled;
        dialog.querySelector('#fastpic-origRotate').value = fastpicSettings.image.origRotate.value;

        // Настройки image.optimization
        dialog.querySelector('#fastpic-optimizationEnabled').checked = fastpicSettings.image.optimization.enabled;
        dialog.querySelector('#fastpic-jpegQuality').value = fastpicSettings.image.optimization.jpegQuality;

        // Настройки постера
        dialog.querySelector('#fastpic-poster').checked = fastpicSettings.image.poster;

        // Управление видимостью опций изменения размера
        const resizeOptions = dialog.querySelector('#fastpic-resizeOptions');
        resizeOptions.style.display = fastpicSettings.image.origResize.enabled ? 'block' : 'none';
        dialog.querySelector('#fastpic-origResizeEnabled').addEventListener('change', (e) => {
            resizeOptions.style.display = e.target.checked ? 'block' : 'none';
        });

        // Управление видимостью опций поворота
        const rotateOptions = dialog.querySelector('#fastpic-rotateOptions');
        rotateOptions.style.display = fastpicSettings.image.origRotate.enabled ? 'block' : 'none';
        dialog.querySelector('#fastpic-origRotateEnabled').addEventListener('change', (e) => {
            rotateOptions.style.display = e.target.checked ? 'block' : 'none';
        });

        // Управление видимостью опций оптимизации
        const optimizationOptions = dialog.querySelector('#fastpic-optimizationOptions');
        optimizationOptions.style.display = fastpicSettings.image.optimization.enabled ? 'block' : 'none';
        dialog.querySelector('#fastpic-optimizationEnabled').addEventListener('change', (e) => {
            optimizationOptions.style.display = e.target.checked ? 'block' : 'none';
        });

        // Обработчик изменения типа превью
        const thumbTypeSelect = dialog.querySelector('#fastpic-checkThumb');
        const thumbTextContainer = dialog.querySelector('#fastpic-thumbText-container');
        function updateThumbTextVisibility() {
            thumbTextContainer.style.display = thumbTypeSelect.value === 'text' ? 'block' : 'none';
        }
        thumbTypeSelect.addEventListener('change', updateThumbTextVisibility);
        updateThumbTextVisibility(); // Устанавливаем начальное состояние

        // Синхронизация предустановленного и пользовательского размера
        const resSelect = dialog.querySelector('#fastpic-resSelect');
        const customSize = dialog.querySelector('#fastpic-customSize');
        // Обработчик изменения предустановленного размера
        resSelect.addEventListener('change', (e) => {
            customSize.value = e.target.value;
        });
        // Начальная синхронизация при открытии диалога
        customSize.value = resSelect.value;

        // ImgBB настройки
        dialog.querySelector('#imgbb-apiKey').value = settings.imgbb.apiKey;
        dialog.querySelector('#imgbb-codeFormat').value = settings.imgbb.codeFormat;
        dialog.querySelector('#imgbb-expiration').value = settings.imgbb.expiration;
        dialog.querySelector('#imgbb-useOriginalFilename').checked = settings.imgbb.useOriginalFilename;

        // ImgBam настройки
        dialog.querySelector('#imagebam-codeFormat').value = settings.imagebam.codeFormat;

        // Управление видимостью настроек сервисов
        const updateServiceSettings = () => {
            const service = dialog.querySelector('#uploadService').value;
            document.querySelectorAll('.service-settings').forEach(el => {
                el.classList.remove('active');
            });
            dialog.querySelector(`#${service}-settings`).classList.add('active');
        };

        dialog.querySelector('#uploadService').addEventListener('change', updateServiceSettings);
        updateServiceSettings();

        // imagebam-galleryEnabled
        const galleryCheckbox = dialog.querySelector('#imagebam-galleryEnabled');
        const galleryOptions = dialog.querySelector('#imagebam-gallery-options');
        galleryCheckbox.addEventListener('change', () => {
            galleryOptions.style.display = galleryCheckbox.checked ? 'block' : 'none';
        });

        // Заполнение значений imagebam
        dialog.querySelector('#imagebam-thumbnailSize').value = settings.imagebam.thumbnailSize;
        dialog.querySelector('#imagebam-contentType').value = settings.imagebam.contentType;
        dialog.querySelector('#imagebam-galleryEnabled').checked = settings.imagebam.galleryEnabled;
        dialog.querySelector('#imagebam-galleryTitle').value = settings.imagebam.galleryTitle;
        galleryOptions.style.display = settings.imagebam.galleryEnabled ? 'block' : 'none';

        // Обработчики кнопок
        dialog.querySelector('#cancelSettings').addEventListener('click', () => {
            overlay.remove();
            dialog.remove();
        });

        dialog.querySelector('#saveSettings').addEventListener('click', () => {
            settings.uploadService = dialog.querySelector('#uploadService').value;

            // Сохраняем настройки FastPic
            settings.fastpic = {
                codeFormat: dialog.querySelector('#fastpic-codeFormat').value,
                thumb: {
                    checkThumb: dialog.querySelector('#fastpic-checkThumb').value,
                    thumbText: dialog.querySelector('#fastpic-thumbText').value,
                    thumbSize: dialog.querySelector('#fastpic-thumbSize').value,
                    thumbSizeVertical: dialog.querySelector('#fastpic-thumbSizeVertical').checked
                },
                image: {
                    origResize: {
                        enabled: dialog.querySelector('#fastpic-origResizeEnabled').checked,
                        resSelect: dialog.querySelector('#fastpic-resSelect').value,
                        customSize: dialog.querySelector('#fastpic-customSize').value
                    },
                    origRotate: {
                        enabled: dialog.querySelector('#fastpic-origRotateEnabled').checked,
                        value: dialog.querySelector('#fastpic-origRotate').value
                    },
                    optimization: {
                        enabled: dialog.querySelector('#fastpic-optimizationEnabled').checked,
                        jpegQuality: dialog.querySelector('#fastpic-jpegQuality').value
                    },
                    poster: dialog.querySelector('#fastpic-poster').checked
                }
            };

            // Сохраняем настройки ImgBB
            settings.imgbb = {
                apiKey: dialog.querySelector('#imgbb-apiKey').value,
                codeFormat: dialog.querySelector('#imgbb-codeFormat').value,
                expiration: dialog.querySelector('#imgbb-expiration').value,
                useOriginalFilename: dialog.querySelector('#imgbb-useOriginalFilename').checked
            };

            // Сохраняем настройки ImageBam
            settings.imagebam = {
                codeFormat: dialog.querySelector('#imagebam-codeFormat').value,
                thumbnailSize: dialog.querySelector('#imagebam-thumbnailSize').value,
                contentType: dialog.querySelector('#imagebam-contentType').value,
                galleryEnabled: dialog.querySelector('#imagebam-galleryEnabled').checked,
                galleryTitle: dialog.querySelector('#imagebam-galleryTitle').value
            };

            saveSettings();
            overlay.remove();
            dialog.remove();

            showNotification('Настройки сохранены');
        });
    }

    // Функция настройки кнопки загрузки
    function setupUploadButton() {
        const site = getCurrentSite();
        if (!site) return;

        // Создаем input для файлов
        const input = document.createElement('input');
        input.type = 'file';
        input.multiple = true;
        input.accept = 'image/*';
        input.style.display = 'none';
        document.body.appendChild(input);

        // Находим существующую кнопку загрузки изображений или добавляем новую
        let uploadButton;
        switch(site) {
            case 'rutracker':
                uploadButton = document.querySelector('#load-pic-btn');
                break;
            case 'tapochek':
                // Создаем новую кнопку для tapochek
                uploadButton = document.createElement('input');
                uploadButton.type = 'button';
                uploadButton.value = 'Загрузить картинку';
                uploadButton.style.cssText = 'margin: 0 5px;';
                const tapochekNav = document.querySelector('.mrg_4.tCenter');
                if (tapochekNav) {
                    // Вставляем в начало
                    tapochekNav.insertBefore(uploadButton, tapochekNav.firstChild);
                }
                break;
            case 'nnmclub':
                // Создаем новую кнопку для nnmclub
                uploadButton = document.createElement('input');
                uploadButton.type = 'button';
                uploadButton.value = 'Загрузить картинку';
                uploadButton.style.cssText = 'margin: 0 5px;';
                const nnmNav = document.querySelector('td.row2[align="center"][valign="middle"][style*="padding: 6px"]');
                if (nnmNav) {
                    // Вставляем в начало
                    nnmNav.insertBefore(uploadButton, nnmNav.firstChild);
                }
                break;
            case '4pda':
                // Создаем новую кнопку для 4pda
                uploadButton = document.createElement('input');
                uploadButton.type = 'button';
                uploadButton.value = 'Загрузить картинку';
                uploadButton.className = 'zbtn zbtn-default';  // Используем стили 4pda
                uploadButton.style.cssText = 'margin: 0 5px;';
                const pdaNav = document.querySelector('.dfrms.text-center') || document.querySelector('div[style*="margin-top: 3px"]');
                if (pdaNav) {
                    // Вставляем в начало
                    pdaNav.insertBefore(uploadButton, pdaNav.firstChild);
                }
                break;
        }

        if (uploadButton) {
            uploadButton.onclick = (e) => {
                e.preventDefault();
                input.click();
            };

            // Добавляем обработчик выбора файлов
            input.addEventListener('change', async (e) => {
                const files = Array.from(e.target.files).filter(file => file.type.startsWith('image/'));
                if (files.length > 0) {
                    await handleImageUpload(files);
                }
                input.value = '';  // Сброс input для возможности повторной загрузки тех же файлов
            });
        }
    }

    // Настройка drag&drop для textarea
    function setupDragAndDrop() {
        const textarea = findTextarea();
        if (!textarea) return;

        textarea.addEventListener('dragover', (e) => {
            e.preventDefault();
            e.stopPropagation();
            textarea.style.border = '2px dashed #4a90e2';
        });

        textarea.addEventListener('dragleave', (e) => {
            e.preventDefault();
            e.stopPropagation();
            textarea.style.border = '';
        });

        textarea.addEventListener('drop', async (e) => {
            e.preventDefault();
            e.stopPropagation();
            textarea.style.border = '';

            const files = Array.from(e.dataTransfer.files).filter(file => file.type.startsWith('image/'));
            if (files.length > 0) {
                await handleImageUpload(files);
            }
        });
    }

    // Функция сохранения настроек
    function saveSettings() {
        GM_setValue('fastpicSettings', settings);
    }

    // Инициализация скрипта
    function initializeScript() {
        addScriptStyles();
        createSettingsMenu();
        setupUploadButton();
        setupDragAndDrop();
    }

    // Загрузка сохраненных настроек
    let settings = Object.assign({}, DEFAULT_SETTINGS, GM_getValue('fastpicSettings', {}));

    // Вызов инициализации
    initializeScript();
})();