Resize Image Downloader

Download resized version of single-image pages

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

You will need to install an extension such as Tampermonkey to install this script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name         Resize Image Downloader
// @version      2.0
// @author       SleepingGiant
// @description  Download resized version of single-image pages
// @namespace    https://greasyfork.org/users/1395131
// @match        *://*/*.jpg
// @match        *://*/*.jpeg
// @match        *://*/*.png
// @match        *://*/*.webp
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    let cropLines = {};
    let cropOverlay = null;
    let infoBox = null;

    const createUI = () => {
        const container = document.createElement('div');
        container.style.position = 'fixed';
        container.style.top = '20px';
        container.style.right = '20px';
        container.style.backgroundColor = '#222';
        container.style.color = 'white';
        container.style.padding = '10px';
        container.style.borderRadius = '10px';
        container.style.zIndex = '9999';
        container.style.display = 'flex';
        container.style.flexDirection = 'column';
        container.style.gap = '6px';
        container.style.fontSize = '13px';

        const createLabeledInput = (labelText, defaultValue) => {
            const wrapper = document.createElement('div');
            wrapper.style.display = 'flex';
            wrapper.style.alignItems = 'center';
            wrapper.style.gap = '4px';

            const label = document.createElement('label');
            label.textContent = labelText;
            const input = document.createElement('input');
            input.type = 'number';
            input.value = defaultValue;
            input.style.width = '60px';
            input.style.padding = '4px';
            input.style.borderRadius = '4px';
            input.style.border = 'none';

            wrapper.appendChild(label);
            wrapper.appendChild(input);
            container.appendChild(wrapper);

            return input;
        };

        const widthInput = createLabeledInput('Width:', 1200);
        const heightInput = createLabeledInput('Height:', 1200);

        // Crop toggle checkbox
        const cropToggle = document.createElement('label');
        cropToggle.style.display = 'flex';
        cropToggle.style.alignItems = 'center';
        cropToggle.style.gap = '6px';
        const cropCheckbox = document.createElement('input');
        cropCheckbox.type = 'checkbox';
        cropCheckbox.checked = false;
        cropCheckbox.onchange = () => {
            if (cropCheckbox.checked) {
                const img = document.querySelector('img');
                if (img) createCropOverlay(img);
            } else {
                removeCropOverlay();
            }
        };
        cropToggle.appendChild(cropCheckbox);
        cropToggle.appendChild(document.createTextNode('Enable Crop'));
        container.appendChild(cropToggle);

        // Scaling toggle checkbox
        const scaleToggle = document.createElement('label');
        scaleToggle.style.display = 'flex';
        scaleToggle.style.alignItems = 'center';
        scaleToggle.style.gap = '6px';
        const scaleCheckbox = document.createElement('input');
        scaleCheckbox.type = 'checkbox';
        scaleCheckbox.checked = true;
        scaleToggle.appendChild(scaleCheckbox);
        scaleToggle.appendChild(document.createTextNode('Enable Scaling'));
        container.appendChild(scaleToggle);


        // Download button
        const downloadBtn = document.createElement('button');
        downloadBtn.textContent = '⬇️ Download';
        styleButton(downloadBtn);
        downloadBtn.onclick = () =>
            downloadCroppedAndResized(
                widthInput.value,
                heightInput.value,
                cropCheckbox.checked,
                scaleCheckbox.checked
            );


        container.appendChild(downloadBtn);
        document.body.appendChild(container);
    };

    const styleButton = (btn) => {
        btn.style.backgroundColor = '#007acc';
        btn.style.color = 'white';
        btn.style.border = 'none';
        btn.style.padding = '6px 10px';
        btn.style.borderRadius = '6px';
        btn.style.cursor = 'pointer';
        btn.style.fontSize = '13px';
    };

    const createCropOverlay = (img) => {
        const rect = img.getBoundingClientRect();

        cropOverlay = document.createElement('div');
        cropOverlay.style.position = 'absolute';
        cropOverlay.style.left = `${rect.left + window.scrollX}px`;
        cropOverlay.style.top = `${rect.top + window.scrollY}px`;
        cropOverlay.style.width = `${rect.width}px`;
        cropOverlay.style.height = `${rect.height}px`;
        cropOverlay.style.zIndex = '9998';
        cropOverlay.style.pointerEvents = 'none';

        document.body.appendChild(cropOverlay);

        const directions = ['left', 'right', 'top', 'bottom'];
        directions.forEach(dir => {
            const line = document.createElement('div');
            line.className = `crop-line ${dir}`;
            Object.assign(line.style, {
                position: 'absolute',
                backgroundColor: 'red',
                pointerEvents: 'auto',
                zIndex: '10000',
                cursor: dir === 'left' || dir === 'right' ? 'ew-resize' : 'ns-resize',
            });

            if (dir === 'left' || dir === 'right') {
                line.style.top = '0';
                line.style.width = '2px';
                line.style.height = '100%';
                line.style.left = dir === 'left' ? '10px' : `${rect.width - 10}px`;
            } else {
                line.style.left = '0';
                line.style.height = '2px';
                line.style.width = '100%';
                line.style.top = dir === 'top' ? '10px' : `${rect.height - 10}px`;
            }

            makeDraggable(line, dir, img, rect);
            cropOverlay.appendChild(line);
            cropLines[dir] = line;
        });

        // Info box
        infoBox = document.createElement('div');
        infoBox.style.position = 'fixed';
        infoBox.style.bottom = '20px';
        infoBox.style.left = '20px';
        infoBox.style.background = '#000a';
        infoBox.style.color = 'white';
        infoBox.style.padding = '6px 10px';
        infoBox.style.borderRadius = '8px';
        infoBox.style.fontSize = '13px';
        infoBox.style.zIndex = '9999';
        document.body.appendChild(infoBox);

        updateInfoBox(img, rect);
    };

    const removeCropOverlay = () => {
        if (cropOverlay) cropOverlay.remove();
        if (infoBox) infoBox.remove();
        cropOverlay = null;
        infoBox = null;
        cropLines = {};
    };

    const makeDraggable = (element, direction, img, imgRect) => {
        let isDragging = false;

        element.addEventListener('mousedown', (e) => {
            e.preventDefault();
            isDragging = true;

            const onMouseMove = (moveEvent) => {
                if (!isDragging) return;

                const delta = direction === 'left' || direction === 'right'
                    ? moveEvent.clientX - imgRect.left
                    : moveEvent.clientY - imgRect.top;

                if (direction === 'left' || direction === 'right') {
                    const clamped = Math.max(0, Math.min(delta, imgRect.width));
                    element.style.left = `${clamped}px`;
                } else {
                    const clamped = Math.max(0, Math.min(delta, imgRect.height));
                    element.style.top = `${clamped}px`;
                }

                updateInfoBox(img, imgRect);
            };

            const onMouseUp = () => {
                isDragging = false;
                document.removeEventListener('mousemove', onMouseMove);
                document.removeEventListener('mouseup', onMouseUp);
            };

            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', onMouseUp);
        });
    };

    const getCropValues = (imgRect) => {
        const left = parseInt(cropLines.left?.style.left || '0');
        const right = parseInt(cropLines.right?.style.left || `${imgRect.width}`);
        const top = parseInt(cropLines.top?.style.top || '0');
        const bottom = parseInt(cropLines.bottom?.style.top || `${imgRect.height}`);

        const cropX = Math.min(left, right);
        const cropY = Math.min(top, bottom);
        const cropW = Math.abs(right - left);
        const cropH = Math.abs(bottom - top);

        return { cropX, cropY, cropW, cropH };
    };

    const updateInfoBox = (img, imgRect) => {
        const { cropX, cropY, cropW, cropH } = getCropValues(imgRect);
        const removedLeft = cropX;
        const removedTop = cropY;
        const removedRight = img.naturalWidth - (cropX + cropW);
        const removedBottom = img.naturalHeight - (cropY + cropH);

        infoBox.innerHTML = `
            <b>Crop</b><br/>
            Removed: Left ${removedLeft}px, Right ${removedRight}px<br/>
            Removed: Top ${removedTop}px, Bottom ${removedBottom}px<br/>
            Final: ${cropW} x ${cropH}px
        `;
    };

    const downloadCroppedAndResized = (newWidth, newHeight, cropEnabled, scaleEnabled) => {
        const img = document.querySelector('img');
        const rect = img.getBoundingClientRect();

        let cropX = 0, cropY = 0, cropW = img.naturalWidth, cropH = img.naturalHeight;

        if (cropEnabled && cropOverlay) {
            const values = getCropValues(rect);
            cropX = values.cropX * img.naturalWidth / rect.width;
            cropY = values.cropY * img.naturalHeight / rect.height;
            cropW = values.cropW * img.naturalWidth / rect.width;
            cropH = values.cropH * img.naturalHeight / rect.height;
        }

        const finalW = scaleEnabled ? parseInt(newWidth) : cropW;
        const finalH = scaleEnabled ? parseInt(newHeight) : cropH;

        const canvas = document.createElement('canvas');
        canvas.width = finalW;
        canvas.height = finalH;
        const ctx = canvas.getContext('2d');

        ctx.drawImage(img, cropX, cropY, cropW, cropH, 0, 0, finalW, finalH);

        canvas.toBlob(blob => {
            const link = document.createElement('a');
            link.href = URL.createObjectURL(blob);
            link.download = `cropped_scaled_${canvas.width}x${canvas.height}.jpg`;
            link.click();
        }, 'image/jpeg', 0.9);
    };


    const interval = setInterval(() => {
        const img = document.querySelector('img');
        if (img && document.body) {
            createUI();
            clearInterval(interval);
        }
    }, 200);
})();