Instagram Auto Scroll (Movable)

Auto-scroll through Instagram feed with movable controls

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name           Instagram Auto Scroll (Movable)
// @icon           https://www.instagram.com/favicon.ico
// @namespace      https://github.com/BlueAG/TamperMonkey/tree/main/Instagram-Auto-Scroll-Movable
// @version        1.0.1
// @description    Auto-scroll through Instagram feed with movable controls
// @author         BlueAG
// @license        MIT
// @match          https://www.instagram.com/*
// @match          https://instagram.com/*
// @grant          none
// ==/UserScript==

(function() {
    'use strict';

    let autoScrollEnabled = false;
    let scrollSpeed = 500; // milliseconds between scrolls 1000
    let scrollInterval;
    let controls;
    let isDragging = false;
    let dragOffset = { x: 0, y: 0 };

    function createControls() {
        controls = document.createElement('div');
        controls.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: rgba(0, 0, 0, 0.8);
            color: white;
            padding: 10px;
            border-radius: 10px;
            z-index: 10000;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            font-size: 14px;
            min-width: 100px;
            backdrop-filter: blur(0px);
            border: 1px solid rgba(255, 255, 255, 0.1);
            cursor: move;
            user-select: none;
        `;

        controls.innerHTML = `
            <div style="margin-bottom: 10px; font-weight: 600; cursor: move; display: flex; justify-content: space-between; align-items: center;">
                <span>Auto Scroll</span>

            </div>
            <button id="toggleScroll" style="background: #0095f6; color: white; border: none; padding: 8px 8px; border-radius: 5px; cursor: pointer; margin-right: 15px; width: 100%; margin-bottom: 8px;">Start</button>

            <div style="margin-bottom: 8px;">
                <label style="display: block; margin-bottom: 4px; font-size: 12px;">Delay (seconds):</label>

                <input type="range" id="speedSlider" min="0.5" max="5.0" step="0.5" value="1.0" style="width: 100%;">

                <span id="speedValue" style="font-size: 12px;">1.0s</span>
            </div>

        `;

        document.body.appendChild(controls);

        // Add event listeners for controls
        document.getElementById('toggleScroll').addEventListener('click', toggleAutoScroll);
        document.getElementById('speedSlider').addEventListener('input', updateSpeed);

        // Make controls draggable
        makeDraggable(controls);
    }

    function makeDraggable(element) {
        const header = element.querySelector('div');

        header.addEventListener('mousedown', startDrag);
        document.addEventListener('mousemove', onDrag);
        document.addEventListener('mouseup', stopDrag);

        function startDrag(e) {
            if (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON') {
                return; // Don't start drag if clicking on interactive elements
            }

            isDragging = true;
            const rect = element.getBoundingClientRect();
            dragOffset.x = e.clientX - rect.left;
            dragOffset.y = e.clientY - rect.top;

            element.style.transition = 'none';
            element.style.cursor = 'grabbing';
            e.preventDefault();
        }

        function onDrag(e) {
            if (!isDragging) return;

            const x = e.clientX - dragOffset.x;
            const y = e.clientY - dragOffset.y;

            // Keep within viewport bounds
            const maxX = window.innerWidth - element.offsetWidth;
            const maxY = window.innerHeight - element.offsetHeight;

            element.style.left = Math.max(0, Math.min(x, maxX)) + 'px';
            element.style.top = Math.max(0, Math.min(y, maxY)) + 'px';
            element.style.right = 'auto';
        }

        function stopDrag() {
            if (isDragging) {
                isDragging = false;
                element.style.cursor = 'move';

                // Save position to localStorage
                savePosition();
            }
        }

        // Touch support for mobile
        header.addEventListener('touchstart', (e) => {
            if (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON') return;

            isDragging = true;
            const touch = e.touches[0];
            const rect = element.getBoundingClientRect();
            dragOffset.x = touch.clientX - rect.left;
            dragOffset.y = touch.clientY - rect.top;

            element.style.transition = 'none';
            e.preventDefault();
        });

        document.addEventListener('touchmove', (e) => {
            if (!isDragging) return;

            const touch = e.touches[0];
            const x = touch.clientX - dragOffset.x;
            const y = touch.clientY - dragOffset.y;

            const maxX = window.innerWidth - element.offsetWidth;
            const maxY = window.innerHeight - element.offsetHeight;

            element.style.left = Math.max(0, Math.min(x, maxX)) + 'px';
            element.style.top = Math.max(0, Math.min(y, maxY)) + 'px';
            element.style.right = 'auto';

            e.preventDefault();
        });

        document.addEventListener('touchend', stopDrag);
    }

    function savePosition() {
        const rect = controls.getBoundingClientRect();
        const position = {
            x: rect.left,
            y: rect.top
        };
        localStorage.setItem('instagramAutoScrollPosition', JSON.stringify(position));
    }

    function loadPosition() {
        if (!controls) return;

        const saved = localStorage.getItem('instagramAutoScrollPosition');
        if (saved) {
            try {
                const position = JSON.parse(saved);
                const maxX = window.innerWidth - controls.offsetWidth;
                const maxY = window.innerHeight - controls.offsetHeight;

                controls.style.left = Math.max(0, Math.min(position.x, maxX)) + 'px';
                controls.style.top = Math.max(0, Math.min(position.y, maxY)) + 'px';
                controls.style.right = 'auto';
            } catch (e) {
                console.log('Could not load saved position');
            }
        }
    }


    // Add these variables at the top with your other declarations
    let scrollTimeout = null;
    let isScrolling = false;
    const SCROLL_ANIMATION_TIME = 300; // Approximate time for smooth scroll in ms

    function toggleAutoScroll() {
        autoScrollEnabled = !autoScrollEnabled;
        const button = document.getElementById('toggleScroll');

        if (autoScrollEnabled) {
            button.textContent = 'Stop';
            button.style.background = '#ed4956';
            startAutoScroll();
        } else {
            button.textContent = 'Start';
            button.style.background = '#0095f6';
            stopAutoScroll();
        }
    }

    function updateSpeed() {
        const slider = document.getElementById('speedSlider');
        const value = document.getElementById('speedValue');

        // Get the value and ensure it's properly formatted
        let speedVal = parseFloat(slider.value);

        // Round to nearest 0.5 to avoid floating point issues
        speedVal = Math.round(speedVal * 2) / 2;

        scrollSpeed = speedVal * 1000; // Convert to milliseconds

        // Format display
        value.textContent = speedVal.toFixed(1) + 's';

        // Update slider to match the rounded value
        slider.value = speedVal;

        // Restart scroll with new speed if active
        if (autoScrollEnabled) {
            stopAutoScroll();
            startAutoScroll();
        }
    }

    function startAutoScroll() {
        function performScroll() {
            if (!autoScrollEnabled) return;

            isScrolling = true;

            // Get the main feed container
            const main = document.querySelector('main') ||
                  document.querySelector('[role="main"]') ||
                  document.querySelector('section main');

            if (main) {
                // Scroll to the next set of posts
                const scrollAmount = window.innerHeight * 0.8;
                window.scrollBy({
                    top: scrollAmount,
                    behavior: 'smooth'
                });
            }

            // Schedule next scroll after scrollSpeed, accounting for animation time
            scrollTimeout = setTimeout(() => {
                isScrolling = false;
                if (autoScrollEnabled) {
                    performScroll();
                }
            }, Math.max(scrollSpeed, SCROLL_ANIMATION_TIME));
        }

        // Start the first scroll
        performScroll();
    }

    function stopAutoScroll() {
        if (scrollTimeout) {
            clearTimeout(scrollTimeout);
            scrollTimeout = null;
        }
        isScrolling = false;
    }

    function init() {
        // Wait for page to load
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', createControls);
        } else {
            createControls();
        }

        // Load saved position after a short delay to ensure controls are rendered
        setTimeout(loadPosition, 100);

        // Stop auto-scroll when user manually scrolls
        // Track manual scrolling more accurately
        let manualScrollTimer;
        window.addEventListener('scroll', () => {
            if (autoScrollEnabled && !isScrolling) {
                // User manually scrolled
                clearTimeout(manualScrollTimer);
                manualScrollTimer = setTimeout(() => {
                    // User stopped manual scrolling
                    if (autoScrollEnabled) {
                        stopAutoScroll();
                        startAutoScroll();
                    }
                }, 500);
            }
        });

        // Stop auto-scroll when page is hidden
        document.addEventListener('visibilitychange', () => {
            if (document.hidden && autoScrollEnabled) {
                stopAutoScroll();
            }
        });

        // Save position when window is resized
        window.addEventListener('resize', () => {
            if (controls) {
                loadPosition(); // Recalculate position within new viewport
            }
        });
    }

    // Initialize when page loads
    init();
})();