Instagram Video Controls - Enhanced State Management

Video controls for Instagram with persistent state and improved muting prevention

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Instagram Video Controls - Enhanced State Management
// @namespace    http://tampermonkey.net/
// @version      2.7
// @description  Video controls for Instagram with persistent state and improved muting prevention
// @match        https://www.instagram.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Immediate execution to handle any pre-existing videos
    const handleExistingVideos = () => {
        document.querySelectorAll('video').forEach(video => {
            video.muted = false;
            video.removeAttribute('muted');

            // Override muted property for existing videos
            Object.defineProperty(video, 'muted', {
                configurable: true,
                get: function() {
                    return this._muted || false;
                },
                set: function(value) {
                    // Only allow unmuting
                    if (value === false) {
                        this._muted = false;
                    }
                    return false;
                }
            });

            // Prevent muted attribute
            const originalSetAttribute = video.setAttribute;
            video.setAttribute = function(name, value) {
                if (name === 'muted') return;
                originalSetAttribute.call(this, name, value);
            };
        });
    };

    // Run immediately for any existing videos
    handleExistingVideos();

    // Early muting prevention for new videos
    const preventMuting = () => {
        const originalCreateElement = document.createElement;
        document.createElement = function(tag) {
            const element = originalCreateElement.call(document, tag);
            if (tag.toLowerCase() === 'video') {
                // Immediately set muted to false
                element.muted = false;

                // Override muted property
                Object.defineProperty(element, 'muted', {
                    configurable: true,
                    get: function() {
                        return this._muted || false;
                    },
                    set: function(value) {
                        // Only allow unmuting
                        if (value === false) {
                            this._muted = false;
                        }
                        return false;
                    }
                });

                // Prevent muted attribute
                const originalSetAttribute = element.setAttribute;
                element.setAttribute = function(name, value) {
                    if (name === 'muted') return;
                    originalSetAttribute.call(this, name, value);
                };
            }
            return element;
        };
    };

    // Additional observer to catch dynamically added video elements
    const observeVideoElements = () => {
        const observer = new MutationObserver((mutations) => {
            mutations.forEach(mutation => {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeName === 'VIDEO') {
                        node.muted = false;
                        node.removeAttribute('muted');
                    }
                });
            });
        });

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

    // Initialize muting prevention immediately
    preventMuting();
    observeVideoElements();


    // UI Constants
    const UI = {
        colors: {
            primary: '#0095f6',
            background: 'rgba(0,0,0,0.8)',
            text: '#ffffff',
            hover: 'rgba(255,255,255,0.1)'
        },
        sizes: {
            buttonSize: '32px',
            fontSize: {
                normal: '14px',
                large: '20px'
            },
            controlHeight: '48px',
            timelineHeight: '3px',
            timelineActiveHeight: '5px'
        },
        styles: {
            button: {
                background: 'none',
                border: 'none',
                color: '#ffffff',
                cursor: 'pointer',
                padding: '0',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                transition: 'opacity 0.2s'
            },
            container: {
                display: 'flex',
                position: 'relative'
            }
        }
    };

    // Video State Management
    const VideoState = {
        preferences: {
            volume: 1,
            speed: 1,
            backgroundPlay: false,
            isMuted: false,
            lastUpdate: Date.now()
        },
        activeControlInstances: new Set(),
        saveTimeout: null,
        lastKnownGoodState: null,

        initialize() {
            try {
                // Load saved preferences
                const saved = localStorage.getItem('igVideoPreferences');
                if (saved) {
                    const parsedPrefs = JSON.parse(saved);
                    // Validate and merge saved preferences
                    this.preferences = {
                        volume: this.isValidVolume(parsedPrefs.volume) ? parsedPrefs.volume : this.preferences.volume,
                        speed: this.isValidSpeed(parsedPrefs.speed) ? parsedPrefs.speed : this.preferences.speed,
                        backgroundPlay: typeof parsedPrefs.backgroundPlay === 'boolean' ? parsedPrefs.backgroundPlay : this.preferences.backgroundPlay,
                        isMuted: typeof parsedPrefs.isMuted === 'boolean' ? parsedPrefs.isMuted : this.preferences.isMuted,
                        lastUpdate: Date.now()
                    };
                    this.lastKnownGoodState = { ...this.preferences };
                }

                // Set up observers
                this.setupLazyLoadDetection();
                this.setupIntersectionObserver();

                window.addEventListener('igVideoStateChange', this.broadcastStateChange.bind(this));
                window.addEventListener('beforeunload', () => {
                    if (this.saveTimeout) {
                        clearTimeout(this.saveTimeout);
                        this.savePreferences();
                    }
                });

            } catch (e) {
                console.error('Error initializing video state:', e);
                this.savePreferences();
            }
        },

        setupLazyLoadDetection() {
            // Watch for new video elements being added
            const videoObserver = new MutationObserver((mutations) => {
                for (const mutation of mutations) {
                    if (mutation.type === 'childList') {
                        mutation.addedNodes.forEach(node => {
                            if (node.nodeName === 'VIDEO') {
                                // Immediately prevent muting for new videos
                                this.preventMutingForVideo(node);

                                // Force state application after a short delay
                                setTimeout(() => {
                                    this.enforceStateOnVideo(node);
                                }, 50); // Small delay to ensure video is initialized
                            } else if (node.querySelectorAll) {
                                // Check for videos in added containers
                                node.querySelectorAll('video').forEach(video => {
                                    this.preventMutingForVideo(video);
                                    setTimeout(() => {
                                        this.enforceStateOnVideo(video);
                                    }, 50);
                                });
                            }
                        });
                    }
                }
            });

            videoObserver.observe(document.body, {
                childList: true,
                subtree: true,
                attributes: true,
                attributeFilter: ['src', 'style']
            });
        },

        setupIntersectionObserver() {
            // Watch for videos coming into view
            const intersectionObserver = new IntersectionObserver((entries) => {
                entries.forEach(entry => {
                    if (entry.isIntersecting && entry.target.nodeName === 'VIDEO') {
                        // When a video comes into view, ensure state is correct
                        this.enforceStateOnVideo(entry.target);
                    }
                });
            }, {
                rootMargin: '50px 0px', // Start checking slightly before video comes into view
                threshold: 0.1
            });

            // Observe all existing videos
            document.querySelectorAll('video').forEach(video => {
                intersectionObserver.observe(video);
            });

            // Watch for new videos to observe
            const videoObserver = new MutationObserver((mutations) => {
                mutations.forEach(mutation => {
                    mutation.addedNodes.forEach(node => {
                        if (node.nodeName === 'VIDEO') {
                            intersectionObserver.observe(node);
                        } else if (node.querySelectorAll) {
                            node.querySelectorAll('video').forEach(video => {
                                intersectionObserver.observe(video);
                            });
                        }
                    });
                });
            });

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

        preventMutingForVideo(video) {
            // Immediate unmuting
            video.muted = false;
            video.removeAttribute('muted');

            // Override muted property
            Object.defineProperty(video, 'muted', {
                configurable: true,
                get: function() {
                    return this._muted || false;
                },
                set: function(value) {
                    // Only allow unmuting
                    if (value === false) {
                        this._muted = false;
                    }
                    return false;
                }
            });

            // Prevent muted attribute
            const originalSetAttribute = video.setAttribute;
            video.setAttribute = function(name, value) {
                if (name === 'muted') return;
                originalSetAttribute.call(this, name, value);
            };
        },

        enforceStateOnVideo(video) {
            if (!video || !this.lastKnownGoodState) return;

            // Find the control instance for this video
            let controlInstance = null;
            this.activeControlInstances.forEach(instance => {
                if (instance.video === video) {
                    controlInstance = instance;
                }
            });

            if (controlInstance) {
                controlInstance.forceApplyState(this.lastKnownGoodState);
            } else {
                // If no control instance exists yet, apply state directly
                video.muted = this.lastKnownGoodState.isMuted;
                video.volume = this.lastKnownGoodState.volume;
                video.playbackRate = this.lastKnownGoodState.speed;
            }
        },

        isValidVolume(vol) {
            return typeof vol === 'number' && vol >= 0 && vol <= 1;
        },

        isValidSpeed(speed) {
            const validSpeeds = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
            return validSpeeds.includes(speed);
        },

        savePreferences() {
            try {
                // Update lastUpdate timestamp
                this.preferences.lastUpdate = Date.now();
                localStorage.setItem('igVideoPreferences', JSON.stringify(this.preferences));
                // Update last known good state
                this.lastKnownGoodState = { ...this.preferences };
            } catch (e) {
                console.error('Error saving preferences:', e);
            }
        },

        update(key, value) {
            if (this.saveTimeout) {
                clearTimeout(this.saveTimeout);
            }

            let isValid = true;
            if (key === 'volume') {
                isValid = this.isValidVolume(value);
            } else if (key === 'speed') {
                isValid = this.isValidSpeed(value);
            }

            if (isValid) {
                this.preferences[key] = value;
                this.preferences.lastUpdate = Date.now();
                this.lastKnownGoodState = { ...this.preferences };

                this.saveTimeout = setTimeout(() => {
                    this.savePreferences();
                    window.dispatchEvent(new CustomEvent('igVideoStateChange', {
                        detail: { ...this.preferences }
                    }));
                }, 300);
            }
        },

        registerInstance(controlInstance) {
            this.activeControlInstances.add(controlInstance);
            controlInstance.applyState(this.preferences);
        },

        unregisterInstance(controlInstance) {
            this.activeControlInstances.delete(controlInstance);
        },

        broadcastStateChange(event) {
            const newState = event.detail;
            this.activeControlInstances.forEach(instance => {
                instance.applyState(newState);
            });
        }
    };


    // DOM Utilities
    const DOMUtils = {
        createButton(options = {}) {
            const button = document.createElement('button');
            button.className = options.className || '';
            Object.assign(button.style, {
                ...UI.styles.button,
                ...options.style
            });
            if (options.innerHTML) button.innerHTML = options.innerHTML;
            if (options.onclick) button.addEventListener('click', options.onclick);
            return button;
        },

        createContainer(options = {}) {
            const container = document.createElement('div');
            container.className = options.className || '';
            Object.assign(container.style, {
                ...UI.styles.container,
                ...options.style
            });
            return container;
        },

        setupHoverMenu(control, menuContainer, delay = 500) {
            let timeout;

            const showMenu = () => {
                clearTimeout(timeout);
                menuContainer.style.display = menuContainer.dataset.displayType || 'block';
            };

            const hideMenu = () => {
                timeout = setTimeout(() => {
                    menuContainer.style.display = 'none';
                }, delay);
            };

            control.addEventListener('mouseenter', showMenu);
            control.addEventListener('mouseleave', hideMenu);
            menuContainer.addEventListener('mouseenter', () => clearTimeout(timeout));
            menuContainer.addEventListener('mouseleave', hideMenu);

            return { showMenu, hideMenu };
        },

        formatTime(seconds) {
            const mins = Math.floor(seconds / 60);
            const secs = Math.floor(seconds % 60);
            return `${mins}:${String(secs).padStart(2, '0')}`;
        }
    };

    // Video Controls Class
    class VideoControls {
        constructor(videoElement) {
            this.video = videoElement;
            this.isDragging = false;
            this.eventListeners = new Set();

            VideoState.registerInstance(this);
            this.container = this.createControlsContainer();
            this.initializeVideoState();
        }

        addEventListener(element, type, handler) {
            element.addEventListener(type, handler);
            this.eventListeners.add({ element, type, handler });
        }

        removeAllEventListeners() {
            this.eventListeners.forEach(({ element, type, handler }) => {
                element.removeEventListener(type, handler);
            });
            this.eventListeners.clear();
        }

        createControlComponent(options) {
            const control = DOMUtils.createContainer({
                className: options.className,
                style: { marginRight: '12px', ...options.style }
            });

            const button = DOMUtils.createButton({
                className: options.buttonClassName,
                style: options.buttonStyle,
                innerHTML: options.buttonContent,
                onclick: options.onClick
            });

            control.appendChild(button);

            if (options.menu) {
                control.appendChild(options.menu);
                if (options.useHoverMenu) {
                    DOMUtils.setupHoverMenu(control, options.menu);
                }
            }

            return { control, button };
        }

        createControlsContainer() {
            const container = DOMUtils.createContainer({
                className: 'ig-video-control',
                style: {
                    width: '100%',
                    height: UI.sizes.controlHeight,
                    background: UI.colors.background,
                    display: 'flex',
                    flexDirection: 'column',
                    zIndex: '9999999',
                    position: 'relative',
                    pointerEvents: 'all'
                }
            });

            const timeline = this.createTimeline();
            const controls = this.createControlsRow();

            container.appendChild(timeline);
            container.appendChild(controls);

            return container;
        }

        createControlsRow() {
            const row = DOMUtils.createContainer({
                className: 'ig-video-controls-row',
                style: {
                    display: 'flex',
                    alignItems: 'center',
                    padding: '0 12px',
                    height: '28px',
                    position: 'relative',
                    zIndex: '9999999'
                }
            });

            const controls = [
                this.createPlayButton(),
                this.createTimeDisplay(),
                this.createSpeedControl(),
                this.createBackgroundPlayControl(),
                this.createVolumeControl()
            ];

            controls.forEach(control => row.appendChild(control));
            return row;
        }

        createPlayButton() {
            const updatePlayButton = (button) => {
                button.innerHTML = this.video.paused ? '⏵️' : '⏸️';
            };

            const { control, button } = this.createControlComponent({
                className: 'ig-video-play-control',
                buttonClassName: 'ig-video-control-button',
                buttonStyle: {
                    fontSize: '24px',
                    width: UI.sizes.buttonSize,
                    height: UI.sizes.buttonSize
                },
                buttonContent: this.video.paused ? '⏵️' : '⏸️',
                onClick: (e) => {
                    e.stopPropagation();
                    if (this.video.paused) {
                        this.video.play();
                    } else {
                        this.video.pause();
                    }
                }
            });

            this.addEventListener(this.video, 'play', () => updatePlayButton(button));
            this.addEventListener(this.video, 'pause', () => updatePlayButton(button));

            return control;
        }

        createTimeDisplay() {
            const display = document.createElement('span');
            display.className = 'ig-video-time-display';
            Object.assign(display.style, {
                color: UI.colors.text,
                fontSize: UI.sizes.fontSize.normal,
                marginRight: '12px',
                fontFamily: 'monospace'
            });

            const updateTime = () => {
                const current = Math.floor(this.video.currentTime);
                const total = Math.floor(this.video.duration);
                display.textContent = `${DOMUtils.formatTime(current)} / ${DOMUtils.formatTime(total)}`;
            };

            this.addEventListener(this.video, 'timeupdate', updateTime);
            this.addEventListener(this.video, 'loadedmetadata', updateTime);
            updateTime();

            return display;
        }

        createSpeedControl() {
            const options = this.createSpeedOptions();
            options.style.display = 'none';

            return this.createControlComponent({
                className: 'ig-video-speed-control',
                buttonClassName: 'ig-video-speed-button',
                buttonStyle: {
                    fontSize: UI.sizes.fontSize.normal,
                    padding: '4px 8px'
                },
                buttonContent: `${this.video.playbackRate}x`,
                menu: options,
                useHoverMenu: false,
                onClick: (e) => {
                    e.stopPropagation();
                    const menu = options;
                    const isVisible = menu.style.display === 'flex';
                    menu.style.display = isVisible ? 'none' : 'flex';

                    if (!isVisible) {
                        const closeMenu = (event) => {
                            if (!menu.contains(event.target)) {
                                menu.style.display = 'none';
                                document.removeEventListener('click', closeMenu);
                            }
                        };
                        setTimeout(() => {
                            document.addEventListener('click', closeMenu);
                        }, 0);
                    }
                }
            }).control;
        }

        createSpeedOptions() {
            const container = document.createElement('div');
            container.className = 'ig-video-speed-options';
            Object.assign(container.style, {
                position: 'absolute',
                top: 'calc(100% + 5px)',
                left: '50%',
                transform: 'translateX(-50%)',
                background: UI.colors.background,
                borderRadius: '4px',
                display: 'none',
                flexDirection: 'column',
                minWidth: '80px',
                boxShadow: '0 2px 8px rgba(0,0,0,0.2)',
                zIndex: '10000001'
            });

            [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2].forEach(speed => {
                const option = DOMUtils.createButton({
                    className: 'ig-video-speed-option',
                    style: {
                        padding: '8px 16px',
                        fontSize: UI.sizes.fontSize.normal,
                        width: '100%',
                        textAlign: 'center',
                        background: 'none',
                        border: 'none',
                        color: '#ffffff',
                        cursor: 'pointer'
                    },
                    innerHTML: `${speed}x`,
                    onclick: (e) => {
                        e.stopPropagation();
                        this.video.playbackRate = speed;
                        VideoState.update('speed', speed);
                        container.style.display = 'none';
                    }
                });

                container.appendChild(option);
            });

            return container;
        }

        createVolumeControl() {
            const sliderContainer = document.createElement('div');
            sliderContainer.className = 'ig-video-volume-slider-container';
            Object.assign(sliderContainer.style, {position: 'absolute',
                left: '25px',
                top: '50%',
                transform: 'translateY(-50%)',
                background: UI.colors.background,
                padding: '10px',
                borderRadius: '4px',
                display: 'none'
            });

            const slider = this.createVolumeSlider();
            sliderContainer.appendChild(slider);

            const { control } = this.createControlComponent({
                className: 'ig-video-volume-control',
                buttonClassName: 'ig-video-volume-button',
                buttonStyle: {
                    fontSize: UI.sizes.fontSize.large,
                    width: UI.sizes.buttonSize,
                    height: UI.sizes.buttonSize
                },
                buttonContent: this.video.muted ? '🔇' : '🔊',
                onClick: (e) => {
                    e.stopPropagation();
                    this.video.muted = !this.video.muted;
                    VideoState.update('isMuted', this.video.muted);
                    this.updateVolumeUI();
                },
                menu: sliderContainer,
                useHoverMenu: true
            });

            return control;
        }

        createVolumeSlider() {
            const slider = document.createElement('input');
            slider.type = 'range';
            slider.min = '0';
            slider.max = '100';
            slider.value = this.video.volume * 100;
            slider.className = 'ig-video-slider';
            Object.assign(slider.style, {
                width: '100px',
                height: '4px',
                background: 'rgba(255,255,255,0.2)',
                cursor: 'pointer'
            });

            this.addEventListener(slider, 'input', (e) => {
                const value = e.target.value / 100;
                this.video.volume = value;
                this.video.muted = value === 0;
                VideoState.update('volume', value);
                VideoState.update('isMuted', this.video.muted);
                this.updateVolumeUI();
            });

            return slider;
        }

        createBackgroundPlayControl() {
            const updateBgPlayButton = (button) => {
                button.innerHTML = VideoState.preferences.backgroundPlay ? '🔓' : '🔒';
                button.title = VideoState.preferences.backgroundPlay ?
                    'Video will continue in background' :
                    'Video will pause in background';
                button.style.opacity = VideoState.preferences.backgroundPlay ? '1' : '0.7';
            };

            const { control, button } = this.createControlComponent({
                className: 'ig-video-bgplay-control',
                buttonClassName: 'ig-video-control-button',
                buttonStyle: {
                    fontSize: UI.sizes.fontSize.normal,
                    padding: '4px 8px',
                    opacity: VideoState.preferences.backgroundPlay ? '1' : '0.7'
                },
                buttonContent: VideoState.preferences.backgroundPlay ? '🔓' : '🔒',
                onClick: (e) => {
                    e.stopPropagation();
                    const newState = !VideoState.preferences.backgroundPlay;
                    VideoState.update('backgroundPlay', newState);
                    updateBgPlayButton(button);
                    newState ? this.enableBackgroundPlay() : this.disableBackgroundPlay();
                }
            });

            return control;
        }

        createTimeline() {
            const timeline = document.createElement('div');
            timeline.className = 'ig-video-timeline';
            Object.assign(timeline.style, {
                width: '100%',
                height: UI.sizes.timelineHeight,
                background: 'rgba(255,255,255,0.2)',
                position: 'relative',
                transition: 'height 0.1s'
            });

            const progress = document.createElement('div');
            progress.className = 'ig-video-progress';
            Object.assign(progress.style, {
                height: '100%',
                background: UI.colors.primary,
                width: '0%',
                position: 'absolute',
                top: '0',
                left: '0'
            });

            const seekHandle = document.createElement('div');
            seekHandle.className = 'ig-video-seek-handle';
            Object.assign(seekHandle.style, {
                width: '12px',
                height: '12px',
                background: UI.colors.primary,
                borderRadius: '50%',
                position: 'absolute',
                right: '-6px',
                top: '50%',
                transform: 'translateY(-50%) scale(0)',
                transition: 'transform 0.1s'
            });

            const tooltip = document.createElement('div');
            tooltip.className = 'ig-video-tooltip';
            Object.assign(tooltip.style, {
                position: 'absolute',
                background: UI.colors.background,
                color: UI.colors.text,
                padding: '4px 8px',
                borderRadius: '4px',
                fontSize: UI.sizes.fontSize.normal,
                bottom: '100%',
                transform: 'translateX(-50%)',
                display: 'none',
                zIndex: '10000000',
                pointerEvents: 'none',
                whiteSpace: 'nowrap',
                marginBottom: '8px'
            });

            progress.appendChild(seekHandle);
            timeline.appendChild(progress);

            const container = DOMUtils.createContainer({
                className: 'ig-video-timeline-container',
                style: {
                    width: '100%',
                    height: '20px',
                    position: 'relative',
                    cursor: 'pointer',
                    padding: '8px 0',
                    zIndex: '9999999'
                }
            });

            container.appendChild(timeline);
            container.appendChild(tooltip);

            this.setupTimelineEvents(container, timeline, progress, seekHandle, tooltip);

            return container;
        }

        setupTimelineEvents(container, timeline, progress, seekHandle, tooltip) {
            const updateTimelinePosition = (e) => {
                if (!this.isDragging) return;

                const rect = timeline.getBoundingClientRect();
                const pos = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
                const newTime = this.video.duration * pos;

                progress.style.width = `${pos * 100}%`;
                tooltip.style.left = `${pos * 100}%`;
                tooltip.textContent = DOMUtils.formatTime(newTime);
                this.video.currentTime = newTime;
            };

            this.addEventListener(container, 'mousedown', (e) => {
                e.stopPropagation();
                this.isDragging = true;
                timeline.style.height = UI.sizes.timelineActiveHeight;
                seekHandle.style.transform = 'translateY(-50%) scale(1)';
                updateTimelinePosition(e);

                const handleMouseMove = (e) => updateTimelinePosition(e);
                const handleMouseUp = () => {
                    this.isDragging = false;
                    timeline.style.height = UI.sizes.timelineHeight;
                    seekHandle.style.transform = 'translateY(-50%) scale(0)';
                    document.removeEventListener('mousemove', handleMouseMove);
                    document.removeEventListener('mouseup', handleMouseUp);
                };

                document.addEventListener('mousemove', handleMouseMove);
                document.addEventListener('mouseup', handleMouseUp);
            });

            this.addEventListener(container, 'mousemove', (e) => {
                const rect = timeline.getBoundingClientRect();
                const pos = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));

                if (!this.isDragging) {
                    tooltip.style.display = 'block';
                    tooltip.style.left = `${pos * 100}%`;
                    const previewTime = this.video.duration * pos;
                    tooltip.textContent = DOMUtils.formatTime(previewTime);
                }
            });

            this.addEventListener(container, 'mouseleave', () => {
                if (!this.isDragging) {
                    tooltip.style.display = 'none';
                    timeline.style.height = UI.sizes.timelineHeight;
                    seekHandle.style.transform = 'translateY(-50%) scale(0)';
                }
            });

            this.addEventListener(container, 'mouseenter', () => {
                if (!this.isDragging) {
                    timeline.style.height = UI.sizes.timelineActiveHeight;
                    seekHandle.style.transform = 'translateY(-50%) scale(1)';
                }
            });

            this.addEventListener(this.video, 'timeupdate', () => {
                if (!this.isDragging) {
                    const progressValue = (this.video.currentTime / this.video.duration) * 100;
                    progress.style.width = `${progressValue}%`;
                }
            });
        }

        initializeVideoState() {
            if (this.video) {
                // Force unmute and set initial volume
                this.video.muted = false;
                this.video.removeAttribute('muted');

                if (this.video.volume === 0) {
                    this.video.volume = VideoState.preferences.volume;
                }

                this.setupVideoListeners();
                this.applyState(VideoState.preferences);
            }
        }

        setupVideoListeners() {
            this.addEventListener(this.video, 'volumechange', () => {
                if (this.video.muted !== VideoState.preferences.isMuted) {
                    VideoState.update('isMuted', this.video.muted);
                    this.updateVolumeUI();
                }
                if (!this.video.muted && this.video.volume !== VideoState.preferences.volume) {
                    VideoState.update('volume', this.video.volume);
                    this.updateVolumeUI();
                }
            });

            this.addEventListener(this.video, 'ratechange', () => {
                if (this.video.playbackRate !== VideoState.preferences.speed) {
                    VideoState.update('speed', this.video.playbackRate);
                    this.updateSpeedUI();
                }
            });
        }

        applyState(state) {
            if (this.video) {
                this.video.playbackRate = state.speed;
                this.video.volume = state.volume;
                this.video.muted = state.isMuted;

                if (this.container) {
                    this.updateVolumeUI();
                    this.updateSpeedUI();
                    this.updateBackgroundPlayUI();
                }

                state.backgroundPlay ? this.enableBackgroundPlay() : this.disableBackgroundPlay();
            }
        }

            forceApplyState(state) {
        if (this.video) {
            // Force apply state regardless of current values
            this.video.muted = state.isMuted;
            this.video.volume = state.volume;
            this.video.playbackRate = state.speed;

            // Force update UI elements
            if (this.container) {
                const volumeButton = this.container.querySelector('.ig-video-volume-button');
                const volumeSlider = this.container.querySelector('.ig-video-slider');
                const speedButton = this.container.querySelector('.ig-video-speed-button');
                const bgPlayButton = this.container.querySelector('.ig-video-bgplay-control .ig-video-control-button');

                if (volumeButton) volumeButton.innerHTML = state.isMuted ? '🔇' : '🔊';
                if (volumeSlider) volumeSlider.value = state.isMuted ? 0 : state.volume * 100;
                if (speedButton) speedButton.innerHTML = `${state.speed}x`;
                if (bgPlayButton) {
                    bgPlayButton.innerHTML = state.backgroundPlay ? '🔓' : '🔒';
                    bgPlayButton.style.opacity = state.backgroundPlay ? '1' : '0.7';
                }
            }

            // Enforce background play state
            state.backgroundPlay ? this.enableBackgroundPlay() : this.disableBackgroundPlay();
        }
    }

        updateVolumeUI() {
            const button = this.container.querySelector('.ig-video-volume-button');
            const slider = this.container.querySelector('.ig-video-slider');
            if (button) button.innerHTML = this.video.muted ? '🔇' : '🔊';
            if (slider) slider.value = this.video.muted ? 0 : this.video.volume * 100;
        }

        updateSpeedUI() {
            const button = this.container.querySelector('.ig-video-speed-button');
            if (button) button.innerHTML = `${this.video.playbackRate}x`;
        }

        updateBackgroundPlayUI() {
            const button = this.container.querySelector('.ig-video-bgplay-control .ig-video-control-button');
            if (button) {
                button.innerHTML = VideoState.preferences.backgroundPlay ? '🔓' : '🔒';
                button.style.opacity = VideoState.preferences.backgroundPlay ? '1' : '0.7';
            }
        }

        enableBackgroundPlay() {
            if (!this.video._originalPause) {
                this.video._originalPause = this.video.pause;
                this.video.pause = () => {
                    if (document.visibilityState === 'hidden' && !this.video.ended) {
                        return Promise.resolve();
                    }
                    return this.video._originalPause.call(this.video);
                };
            }
        }

        disableBackgroundPlay() {
            if (this.video._originalPause) {
                this.video.pause = this.video._originalPause;
                delete this.video._originalPause;
            }
        }

        destroy() {
            VideoState.unregisterInstance(this);
            this.removeAllEventListeners();
            if (this.container) {
                this.container.remove();
            }
        }
    }

    // Add global styles
    const addStyles = () => {
        const styles = `
            .ig-video-slider::-webkit-slider-thumb {
                -webkit-appearance: none;
                width: 12px;
                height: 12px;
                background: ${UI.colors.primary};
                border-radius: 50%;
                cursor: pointer;
            }
            .ig-video-slider::-moz-range-thumb {
                width: 12px;
                height: 12px;
                background: ${UI.colors.primary};
                border-radius: 50%;
                cursor: pointer;
                border: none;
            }
            .ig-video-control-button:hover {
                opacity: 0.8;
            }
            .ig-video-speed-option:hover {
                background: ${UI.colors.hover};
            }
        `;

        const styleSheet = document.createElement('style');
        styleSheet.textContent = styles;
        document.head.appendChild(styleSheet);
    };




    // Initialize video controls
    const initVideoControls = () => {
        const processedVideos = new WeakSet();

        const addControlsToVideo = (videoElement) => {
            if (processedVideos.has(videoElement)) return;

            const videoContainer = videoElement.closest('div[class*="x5yr21d"][class*="x1uhb9sk"]');
            if (!videoContainer) return;

            // Early unmuting
            videoElement.muted = false;
            videoElement.removeAttribute('muted');

            processedVideos.add(videoElement);
            const controls = new VideoControls(videoElement);

            const controlsWrapper = DOMUtils.createContainer({
                className: 'ig-video-controls-wrapper',
                style: {
                    width: '100%',
                    position: 'relative',
                    zIndex: '9999999'
                }
            });



            controlsWrapper.appendChild(controls.container);
            videoContainer.parentElement.insertBefore(controlsWrapper, videoContainer);
            videoContainer.style.position = 'relative';
            videoContainer.style.zIndex = '1';

            const observer = new MutationObserver((mutations) => {
                if (!document.contains(videoElement)) {
                    controls.destroy();
                    observer.disconnect();
                }
            });

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

        // Main observer
        const observer = new MutationObserver((mutations) => {
            mutations.forEach(mutation => {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeName === 'VIDEO') {
                        addControlsToVideo(node);
                    } else if (node.querySelectorAll) {
                        node.querySelectorAll('video').forEach(addControlsToVideo);
                    }
                });
                });
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['src', 'style', 'class']
        });

        // Process existing videos
        document.querySelectorAll('video').forEach(addControlsToVideo);



    };

    // Initialize everything
    const initialize = () => {
        VideoState.initialize();
        addStyles();
        initVideoControls();
    };

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }
})();