Gemoo Downloader

Download videos from Gemoo

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Gemoo Downloader
// @version      2024-09-07
// @description  Download videos from Gemoo
// @author       Snoofz
// @match        https://gemoo.com/tools/upload-video/share/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=gemoo.com
// @grant        none
// @namespace https://greasyfork.org/users/1364648
// ==/UserScript==

class Log {
    static info(message) {
        console.log(`%c${message.toUpperCase()}`, 'font-size: 18px; color: #7289da;');
    }

    static tool(message) {
        console.log(`%c${message.toUpperCase()}`, 'font-size: 18px; color: #FFB6C1;');
    }

    static welcome(message) {
        console.log(`%c${message.toUpperCase()}`, 'font-size: 25px; color: #ff0000;');
    }

    static error(message) {
        console.error(`%c${message.toUpperCase()}`, 'font-size: 18px; color: #dc3545;');
    }

    static success(message) {
        console.log(`%c${message.toUpperCase()}`, 'font-size: 18px; color: #28a745;');
    }
}

class UIManager {
    constructor() {
        this.UIContext = null;
        this.UIMenus = [];
        this.tabs = [];
        this.notificationStack = [];
        this.notificationHeight = 100;
        this.notificationMargin = 10;
    }

    getAllTabs() {
        return this.tabs;
    }

    createNotification(titleText, descriptionText) {
        const notificationContainer = document.createElement('div');
        notificationContainer.className = 'notification-popup';
        notificationContainer.style.position = 'fixed';
        notificationContainer.style.left = '10px';
        notificationContainer.style.bottom = this.calculateNotificationBottom() + 'px';
        notificationContainer.style.transform = 'translateY(100%)';
        notificationContainer.style.backgroundColor = '#0e0e0e';
        notificationContainer.style.color = '#ffffff';
        notificationContainer.style.width = '300px';
        notificationContainer.style.padding = '20px';
        notificationContainer.style.borderRadius = '8px';
        notificationContainer.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.5)';
        notificationContainer.style.zIndex = '9999';
        notificationContainer.style.transition = 'transform 0.3s ease-in-out';

        const title = document.createElement('h2');
        title.textContent = titleText;
        title.style.fontSize = '22px';
        title.style.textAlign = 'center';
        title.style.marginBottom = '10px';
        title.classList.add('rainbow-animation');

        const description = document.createElement('p');
        description.textContent = descriptionText;
        description.style.fontSize = '16px';
        description.style.textAlign = 'center';
        description.classList.add('rainbow-animation');

        notificationContainer.appendChild(title);
        notificationContainer.appendChild(description);

        document.body.appendChild(notificationContainer);

        setTimeout(() => {
            notificationContainer.style.transform = 'translateY(0)';
        }, 50);

        setTimeout(() => {
            notificationContainer.style.transform = 'translateY(100%)';
            setTimeout(() => {
                this.removeNotification(notificationContainer);
                document.body.removeChild(notificationContainer);
            }, 300);
        }, 5000);

        this.makeDraggable(notificationContainer);

        this.notificationStack.push(notificationContainer);
    }

    calculateNotificationBottom() {
        let totalHeight = this.notificationMargin;
        this.notificationStack.forEach(notification => {
            totalHeight += notification.offsetHeight + this.notificationMargin;
        });
        return totalHeight;
    }

    removeNotification(notification) {
        const index = this.notificationStack.indexOf(notification);
        if (index !== -1) {
            this.notificationStack.splice(index, 1);
        }
        this.repositionNotifications();
    }

    repositionNotifications() {
        let totalHeight = this.notificationMargin;
        this.notificationStack.forEach(notification => {
            notification.style.bottom = totalHeight + 'px';
            totalHeight += notification.offsetHeight + this.notificationMargin;
        });
    }

    createMenu(elementId, titleText, width = '300px', height = 'auto') {
        const container = document.createElement('div');
        container.id = elementId;
        container.style.position = 'fixed';
        container.style.backgroundColor = '#0e0e0e';
        container.style.borderRadius = '8px';
        container.style.padding = '20px';
        container.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.5)';
        container.style.zIndex = '9999';
        container.style.width = width;
        container.style.height = height;

        container.style.top = `calc(50% - (${height} / 2))`;
        container.style.left = `calc(50% - (${width} / 2))`;

        container.style.userSelect = 'none';

        container.style.overflowY = 'auto';
        container.className = 'custom-scrollbar';

        const title = document.createElement('h2');
        title.textContent = titleText;
        title.style.color = '#ffffff';
        title.style.marginBottom = '20px';
        title.style.fontSize = '22px';
        title.style.textAlign = 'center';
        title.style.marginTop = '0px';
        title.classList.add('rainbow-animation');

        container.appendChild(title);

        document.body.appendChild(container);

        this.UIContext = container;

        return container;
    }

    makeDraggable(element) {
        let offsetX, offsetY;

        function handleMouseDown(event) {
            event.preventDefault();
            const boundingRect = element.getBoundingClientRect();
            offsetX = event.clientX - boundingRect.left;
            offsetY = event.clientY - boundingRect.top;

            console.log(`x: ${event.clientX}, y: ${event.clientY}, Offsetx: ${offsetX}, Offsety: ${offsetY}`)

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

        function handleMouseMove(event) {
            moveElement(event.clientX, event.clientY);
        }

        function moveElement(clientX, clientY) {
            element.style.left = clientX - offsetX + 'px';
            element.style.top = clientY - offsetY + 'px';
        }

        function handleMouseUp() {
            cleanupListeners();
        }

        function handleTouchEnd() {
            cleanupListeners();
        }

        function cleanupListeners() {
            document.removeEventListener('mousemove', handleMouseMove);
            document.removeEventListener('mouseup', handleMouseUp);
        }

        const titleBar = element.querySelector('h2');
        titleBar.addEventListener('mousedown', handleMouseDown);

        element.style.position = 'absolute';
        titleBar.style.cursor = 'move';
        titleBar.style.userSelect = 'none';
    }

    addButton(buttonText, buttonAction) {
        const button = document.createElement('button');
        button.style.width = '100%';
        button.style.padding = '10px';
        button.style.backgroundColor = '#1c1c1c';
        button.style.color = '#ffffff';
        button.style.border = 'none';
        button.style.borderRadius = '5px';
        button.style.cursor = 'pointer';
        button.style.marginBottom = '10px';
        button.style.fontWeight = 'bold';
        button.style.fontSize = '16px';
        button.addEventListener('click', buttonAction);
        button.classList.add('rainbow-animation');

        const buttonTextSpan = document.createElement('span');
        buttonTextSpan.textContent = buttonText;
        button.appendChild(buttonTextSpan);

        this.UIContext.appendChild(button);

        return button;
    }

    addLabel(labelText) {
        const label = document.createElement('h3');
        label.textContent = labelText;
        label.style.color = '#ffffff';
        label.style.marginBottom = '20px';
        label.style.fontSize = '18px';
        label.style.textAlign = 'center';
        label.classList.add('rainbow-animation');

        this.UIContext.appendChild(label);

        return label;
    }

    addSpacer(height) {
        const spacer = document.createElement('div');
        spacer.style.width = '100%';
        spacer.style.height = `${height}px`;
        spacer.style.marginBottom = `${height}px`;

        this.UIContext.appendChild(spacer);

        return spacer;
    }

    addTextInput(placeholderText, valueChangeAction) {
        const input = document.createElement('input');
        input.type = 'text';
        input.placeholder = placeholderText;
        input.style.width = 'calc(100% - 1px)';
        input.style.padding = '10px';
        input.style.marginBottom = '20px';
        input.style.borderRadius = '5px';
        input.addEventListener('input', valueChangeAction);
        input.style.backgroundColor = '#0e0e0e';
        input.classList.add('rainbow-animation');
        input.focus();

        this.UIContext.appendChild(input);
        input.focus();

        return input;
    }

    addSlider(min, max, step, currentValue, customText, valueChangeAction) {
        let textBubble = undefined;
        let hideTimeout = null;

        const sliderContainer = document.createElement('div');
        sliderContainer.style.width = 'calc(100% - 1px)';
        sliderContainer.style.marginBottom = '20px';
        sliderContainer.style.position = 'relative';

        const slider = document.createElement('input');
        slider.type = 'range';
        slider.min = min;
        slider.max = max;
        slider.value = currentValue;
        slider.step = step;
        slider.style.width = '100%';
        slider.style.borderRadius = '5px';

        const showBubble = () => {
            clearTimeout(hideTimeout);
            textBubble.style.opacity = 1;
            hideTimeout = setTimeout(() => {
                textBubble.style.opacity = 0;
            }, 3000);
        };

        slider.addEventListener('input', () => {
            valueChangeAction(slider.value);
            textBubble.textContent = `${customText}: ${slider.value}`;
            const sliderWidth = slider.offsetWidth;
            const bubbleWidth = textBubble.offsetWidth;
            const sliderValue = slider.value;
            const newPosition = (sliderValue / (max - min)) * sliderWidth;
            const adjustedPosition = Math.min(Math.max(newPosition, bubbleWidth / 2), sliderWidth - bubbleWidth / 2);
            textBubble.style.left = `${adjustedPosition}px`;
            showBubble();
        });

        slider.addEventListener('mousedown', showBubble);
        slider.addEventListener('touchstart', showBubble);

        slider.classList.add('rainbow-animation');

        const bubble = document.createElement('div');
        bubble.style.position = 'absolute';
        bubble.style.top = 'calc(100% + 10px)';
        bubble.style.left = '50%';
        bubble.style.transform = 'translateX(-50%)';
        bubble.style.backgroundColor = '#f0f0f0';
        bubble.style.padding = '5px 10px';
        bubble.style.borderRadius = '5px';
        bubble.style.backgroundColor = '#181818';
        bubble.style.whiteSpace = 'nowrap';
        bubble.style.minWidth = '100px';
        bubble.style.transition = 'opacity 0.5s';
        bubble.style.opacity = 0;
        bubble.textContent = `${customText}: ${currentValue}`;
        textBubble = bubble;

        sliderContainer.appendChild(bubble);
        sliderContainer.appendChild(slider);

        this.contentContainer.appendChild(sliderContainer);

        return slider;
    }

    addLogo() {
        const logo = document.createElement('img');
        logo.src = 'https://github.com/Snoofz/Hailware-Assets/blob/main/snowly-icon.png?raw=true';
        logo.className = 'logo';
        logo.alt = 'Logo';
        logo.style.marginLeft = '35%';
        logo.classList.add('hue-shift-animation');

        this.UIContext.insertBefore(logo, this.UIContext.firstChild);

        return logo;
    }

    createTabMenu(tabs) {
        const tabContainer = document.createElement('div');
        tabContainer.style.display = 'flex';
        tabContainer.style.borderBottom = '1px solid #cc0000';
        tabContainer.style.marginBottom = '20px';
        tabContainer.classList.add('rainbow-animation')

        const contentContainers = tabs.map(() => document.createElement('div'));

        tabs.forEach((tab, index) => {
            const tabButton = document.createElement('button');
            tabButton.textContent = tab.title;
            tabButton.style.flex = '1';
            tabButton.style.padding = '10px';
            tabButton.style.backgroundColor = '#1c1c1c';
            tabButton.style.color = '#ffffff';
            tabButton.style.border = 'none';
            tabButton.style.cursor = 'pointer';
            tabButton.style.fontWeight = 'bold';
            tabButton.style.fontSize = '16px';
            tabButton.classList.add('rainbow-animation');

            tabButton.addEventListener('click', () => {
                contentContainers.forEach((container, idx) => {
                    if (idx !== index) {
                        container.style.display = 'none';
                    }
                });
                contentContainers[index].style.display = 'block';
            });

            this.tabs.push(tabButton);
            tabContainer.appendChild(tabButton);

            const uiTab = new UITab(tab.title, contentContainers[index], document.createElement('div'));
            uiTab.content.innerHTML = tab.content;
            tab.uiTab = uiTab;
        });

        this.UIContext.appendChild(tabContainer);

        contentContainers.forEach(container => {
            container.style.display = 'none';
            this.UIContext.appendChild(container);
        });

        if (contentContainers.length > 0) {
            contentContainers[0].style.display = 'block';
        }

        return {
            UITabs: tabs,
            Containers: contentContainers
        };
    }

    addTabsToTabMenu(existingTabs, newTabs) {
        const contentContainers = newTabs.map(() => document.createElement('div'));

        newTabs.forEach((tab, index) => {
            const tabButton = document.createElement('button');
            tabButton.textContent = tab.title;
            tabButton.style.flex = '1';
            tabButton.style.padding = '10px';
            tabButton.style.backgroundColor = '#1c1c1c';
            tabButton.style.color = '#ffffff';
            tabButton.style.border = 'none';
            tabButton.style.cursor = 'pointer';
            tabButton.style.fontWeight = 'bold';
            tabButton.style.fontSize = '16px';
            tabButton.classList.add('rainbow-animation');

            tabButton.addEventListener('click', () => {
                contentContainers.forEach((container, idx) => {
                    if (idx !== index) {
                        container.style.display = 'none';
                    }
                });
                contentContainers[index].style.display = 'block';
            });

            existingTabs.push(tabButton);
            const uiTab = new UITab(tab.title, contentContainers[index], document.createElement('div'));
            uiTab.content.innerHTML = tab.content;
            tab.uiTab = uiTab;
        });

        existingTabs.forEach(tab => {
            this.UIContext.appendChild(tab);
        });

        contentContainers.forEach(container => {
            container.style.display = 'none';
            this.UIContext.appendChild(container);
        });

        if (contentContainers.length > 0) {
            contentContainers[0].style.display = 'block';
        }
    }

    showTabContent(index, tabs, contentContainer) {
        contentContainer.innerHTML = '';

        const content = document.createElement('div');
        content.innerHTML = tabs[index].content;
        content.style.color = '#ffffff';
        content.style.fontSize = '16px';
        contentContainer.appendChild(content);

        this.activeTabContent = content;
    }
}

class UITab {
    constructor(title, contentContainer, content) {
        this.title = title;
        this.contentContainer = contentContainer;
        this.content = content;
        this.isHidden = true;
    }

    static getContentContainer() {
        return this.contentContainer;
    }

    addButton(buttonText, buttonAction) {
        const button = document.createElement('button');
        button.style.width = '100%';
        button.style.padding = '10px';
        button.style.backgroundColor = '#1c1c1c';
        button.style.color = '#ffffff';
        button.style.border = 'none';
        button.style.borderRadius = '5px';
        button.style.cursor = 'pointer';
        button.style.marginBottom = '10px';
        button.style.fontWeight = 'bold';
        button.style.fontSize = '16px';
        button.addEventListener('click', buttonAction);
        button.classList.add('rainbow-animation');

        const buttonTextSpan = document.createElement('span');
        buttonTextSpan.textContent = buttonText;
        button.appendChild(buttonTextSpan);

        this.contentContainer.appendChild(button);

        return button;
    }

    addLabel(labelText) {
        const label = document.createElement('h3');
        label.innerHTML = labelText;
        label.style.color = '#ffffff';
        label.style.marginBottom = '20px';
        label.style.fontSize = '18px';
        label.style.textAlign = 'center';
        label.classList.add('rainbow-animation');

        this.contentContainer.appendChild(label);

        return label;
    }

    addTextInput(placeholderText, valueChangeAction) {
        const input = document.createElement('input');
        input.type = 'text';
        input.placeholder = placeholderText;
        input.style.width = 'calc(100% - 1px)';
        input.style.padding = '10px';
        input.style.marginBottom = '20px';
        input.style.borderRadius = '5px';
        input.addEventListener('input', valueChangeAction);
        input.style.backgroundColor = '#0e0e0e';
        input.classList.add('rainbow-animation');
        input.focus();
        this.contentContainer.appendChild(input);
        input.focus();
        return input;
    }

    addSpacer(height) {
        const spacer = document.createElement('div');
        spacer.style.width = '100%';
        spacer.style.height = `${height}px`;
        spacer.style.marginBottom = `${height}px`;

        this.contentContainer.appendChild(spacer);

        return spacer;
    }

    addSlider(min, max, step, currentValue, customText, valueChangeAction) {
        let textBubble = undefined;
        let hideTimeout = null;

        const sliderContainer = document.createElement('div');
        sliderContainer.style.width = 'calc(100% - 1px)';
        sliderContainer.style.marginBottom = '20px';
        sliderContainer.style.position = 'relative';

        const slider = document.createElement('input');
        slider.type = 'range';
        slider.min = min;
        slider.max = max;
        slider.value = currentValue;
        slider.step = step;
        slider.style.width = '100%';
        slider.style.borderRadius = '5px';

        const showBubble = () => {
            clearTimeout(hideTimeout);
            textBubble.style.opacity = 1;
            hideTimeout = setTimeout(() => {
                textBubble.style.opacity = 0;
            }, 3000);
        };

        slider.addEventListener('input', () => {
            valueChangeAction(slider.value);
            textBubble.textContent = `${customText}: ${slider.value}`;
            const sliderWidth = slider.offsetWidth;
            const bubbleWidth = textBubble.offsetWidth;
            const sliderValue = slider.value;
            const newPosition = (sliderValue / (max - min)) * sliderWidth;
            const adjustedPosition = Math.min(Math.max(newPosition, bubbleWidth / 2), sliderWidth - bubbleWidth / 2);
            textBubble.style.left = `${adjustedPosition}px`;
            showBubble();
        });

        slider.addEventListener('mousedown', showBubble);
        slider.addEventListener('touchstart', showBubble);

        slider.classList.add('rainbow-animation');

        const bubble = document.createElement('div');
        bubble.style.position = 'absolute';
        bubble.style.top = 'calc(100% + 10px)';
        bubble.style.left = '50%';
        bubble.style.transform = 'translateX(-50%)';
        bubble.style.backgroundColor = '#f0f0f0';
        bubble.style.padding = '5px 10px';
        bubble.style.borderRadius = '5px';
        bubble.style.backgroundColor = '#181818';
        bubble.style.whiteSpace = 'nowrap';
        bubble.style.minWidth = '100px';
        bubble.style.transition = 'opacity 0.5s';
        bubble.style.opacity = 0;
        bubble.textContent = `${customText}: ${currentValue}`;
        textBubble = bubble;

        sliderContainer.appendChild(bubble);
        sliderContainer.appendChild(slider);

        this.contentContainer.appendChild(sliderContainer);

        return slider;
    }

    showContent() {
        const allTabs = this.contentContainer.parentElement.querySelectorAll('.tab-content');
        allTabs.forEach(tab => {
            tab.style.display = 'none';
        });

        if (this.isHidden) {
            this.contentContainer.style.display = 'block';
            this.isHidden = false;
        }
    }
}

function loadScript(url) {
    return new Promise((resolve, reject) => {
        const script = document.createElement('script');
        script.src = url;
        script.onload = () => resolve();
        script.onerror = () => reject(new Error(`Failed to load script ${url}`));
        document.head.appendChild(script);
    });
}

loadScript('https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.36/Tone.min.js')
    .then(() => {
        console.log('Tone.js loaded');
        Log.welcome("Gemoo Downloader");

        function isPlayerInArray(playerName) {
            return players.some(playerElement => playerElement.textContent.includes(playerName));
        }
        let uiManager = new UIManager();

        let mainMenu = uiManager.createMenu("epicUI", "Gemoo Downloader", "250px", "165px");
        uiManager.makeDraggable(mainMenu);

        let tabs = uiManager.createTabMenu([{
            title: 'Main',
            content: '<p>This is the content of Tab 1</p>'
        }]);
        tabs = tabs.UITabs;

        tabs[0].uiTab.addButton("Download Video", () => {
            uiManager.createNotification('Hailware', `Downloading Video...`);
            document.querySelector('.video-play').click();

            const checkVideoSrc = () => {
                const videoElement = document.getElementById('videoId');
                const videoSrc = videoElement.getAttribute('src');

                if (videoSrc) {
                    console.log(videoSrc);

                    fetch(videoSrc)
                        .then(response => response.blob())
                        .then(blob => {
                            const blobUrl = window.URL.createObjectURL(blob);

                            const anchor = document.createElement('a');
                            anchor.href = blobUrl;
                            anchor.download = 'video.mp4';
                            document.body.appendChild(anchor);
                            anchor.click();
                            document.body.removeChild(anchor);

                            window.URL.revokeObjectURL(blobUrl);
                            uiManager.createNotification('Hailware', `Video downloaded! Enjoy!`);
                        })
                        .catch(err => {
                            console.error('Error downloading the video:', err);
                        });

                    clearInterval(interval);

                }
            };

            const interval = setInterval(checkVideoSrc, 500);
        });
    });