Enhanced Catbox File Uploader with Auto-Close Drop Zone and Duration Option

Upload files to Catbox with URL history, customizable duration, minimize support, middle-click to open in new tab, auto-close drop zone if no file is dropped within 3 seconds.

目前為 2024-11-01 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Enhanced Catbox File Uploader with Auto-Close Drop Zone and Duration Option
// @namespace    https://catbox.moe/
// @version      1.1
// @description  Upload files to Catbox with URL history, customizable duration, minimize support, middle-click to open in new tab, auto-close drop zone if no file is dropped within 3 seconds.
// @match        *://litterbox.catbox.moe/*
// @match        *://catbox.moe/*
// @match        *://*/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==
(function() {
    'use strict';

    // Create necessary DOM elements
    const uploadButton = document.createElement('div');
    uploadButton.id = 'uploadButton';
    uploadButton.innerHTML = '⬆';
    document.body.appendChild(uploadButton);

    const fileInput = document.createElement('input');
    fileInput.type = 'file';
    fileInput.style.display = 'none';
    document.body.appendChild(fileInput);

    const urlTextBox = document.createElement('input');
    urlTextBox.type = 'text';
    urlTextBox.id = 'fileUrl';
    urlTextBox.placeholder = 'URL will appear here';
    urlTextBox.readOnly = true;
    urlTextBox.style.display = 'none';
    document.body.appendChild(urlTextBox);

    const copyButton = document.createElement('div');
    copyButton.id = 'copyButton';
    copyButton.innerHTML = '📋';
    copyButton.style.display = 'none';
    document.body.appendChild(copyButton);

    const dropZone = document.createElement('div');
    dropZone.id = 'dropZone';
    dropZone.innerText = 'Drag & Drop File Here';
    dropZone.style.display = 'none';
    document.body.appendChild(dropZone);

    const minimizeButton = document.createElement('div');
    minimizeButton.id = 'minimizeButton';
    minimizeButton.innerHTML = '—';
    minimizeButton.style.display = 'none';
    document.body.appendChild(minimizeButton);

    const historyButton = document.createElement('div');
    historyButton.id = 'historyButton';
    historyButton.innerHTML = '📜';
    historyButton.style.display = 'none';
    document.body.appendChild(historyButton);

    const clearHistoryButton = document.createElement('div');
    clearHistoryButton.id = 'clearHistoryButton';
    clearHistoryButton.innerHTML = '🗑️';
    clearHistoryButton.style.display = 'none';
    document.body.appendChild(clearHistoryButton);

    const historyList = document.createElement('div');
    historyList.id = 'historyList';
    historyList.style.display = 'none';
    document.body.appendChild(historyList);

    const durationButton = document.createElement('div');
    durationButton.id = 'durationButton';
    durationButton.innerHTML = GM_getValue('uploadDuration', '1h');  // Display initial duration
    durationButton.style.display = 'none';
    document.body.appendChild(durationButton);

    const durationMenu = document.createElement('div');
    durationMenu.id = 'durationMenu';
    durationMenu.style.display = 'none';
    durationMenu.innerHTML = `
        <div data-duration="1h">1 hour</div>
        <div data-duration="12h">12 hours</div>
        <div data-duration="24h">24 hours</div>
        <div data-duration="72h">72 hours</div>
    `;
    document.body.appendChild(durationMenu);

    GM_addStyle(`
        #uploadButton, #historyButton, #clearHistoryButton, #minimizeButton, #durationButton {
            position: fixed;
            bottom: 20px;
            width: 50px;
            height: 50px;
            background-color: #333;
            color: white;
            border: none;
            border-radius: 50%;
            cursor: pointer;
            font-family: Arial, sans-serif;
            font-size: 24px;
            text-align: center;
            line-height: 50px;
            box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2);
            z-index: 10000;
        }
        #uploadButton { left: 20px; }
        #uploadButton.minimized {
            bottom: 0;
            width: 50px;
            height: 10px;
            border-radius: 50px 50px 0 0;
            font-size: 10px;
            line-height: 10px;
        }
        #minimizeButton, #historyButton, #clearHistoryButton, #durationButton {
            width: 40px;
            height: 40px;
            font-size: 20px;
            line-height: 40px;
        }
        #minimizeButton { left: 80px; }
        #historyButton { left: 140px; }
        #clearHistoryButton { left: 200px; }
        #durationButton { left: 260px; }

        #fileUrl, #historyList, #durationMenu {
            position: fixed;
            bottom: 80px;
            left: 20px;
            width: 270px;
            background-color: #333;
            color: white;
            padding: 10px;
            border: none;
            border-radius: 5px;
            font-size: 14px;
            box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2);
            z-index: 10000;
            overflow-y: auto;
        }
        #fileUrl { display: block; }
        #historyList {
            height: 200px;
            display: none;
        }
        #historyList div {
            margin-bottom: 10px;
        }
        #historyList div span.timestamp {
            display: block;
            color: #aaa;
            font-size: 12px;
            margin-top: 4px;
            border-top: 1px solid #555;
            padding-top: 2px;
        }
        #historyList a {
            color: #66ccff;
            text-decoration: none;
        }
        #copyButton {
            position: fixed;
            bottom: 80px;
            left: 300px;
            width: 30px;
            height: 30px;
            background-color: #333;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-family: Arial, sans-serif;
            font-size: 16px;
            text-align: center;
            line-height: 30px;
            box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2);
            z-index: 10000;
        }
        #dropZone {
            position: fixed;
            bottom: 150px;
            left: 20px;
            width: 300px;
            height: 150px;
            border: 2px dashed #aaa;
            background-color: #444;
            color: white;
            text-align: center;
            line-height: 150px;
            font-family: Arial, sans-serif;
            font-size: 14px;
            border-radius: 5px;
            z-index: 10000;
        }
        #dropZone.dragover {
            border-color: #fff;
            background-color: #555;
        }
        #durationMenu div:hover {
            background-color: #555;
        }
    `);

    let isMinimized = true;
    let uploadDuration = GM_getValue('uploadDuration', '1h');
    uploadButton.classList.add('minimized');

    uploadButton.addEventListener('click', () => {
        if (isMinimized) {
            uploadButton.classList.remove('minimized');
            isMinimized = false;
            minimizeButton.style.display = 'block';
            historyButton.style.display = 'block';
            clearHistoryButton.style.display = 'block';
            durationButton.style.display = 'block';
        } else {
            fileInput.click();
        }
    });

    minimizeButton.addEventListener('click', () => {
        uploadButton.classList.add('minimized');
        isMinimized = true;
        minimizeButton.style.display = 'none';
        urlTextBox.style.display = 'none';
        copyButton.style.display = 'none';
        historyList.style.display = 'none';
        historyButton.style.display = 'none';
        clearHistoryButton.style.display = 'none';
        durationButton.style.display = 'none';
        dropZone.style.display = 'none';
        durationMenu.style.display = 'none';
    });

    durationButton.addEventListener('click', () => {
        durationMenu.style.display = durationMenu.style.display === 'none' ? 'block' : 'none';
    });

    durationMenu.addEventListener('click', (e) => {
        if (e.target.dataset.duration) {
            uploadDuration = e.target.dataset.duration;
            GM_setValue('uploadDuration', uploadDuration);
            durationButton.innerHTML = uploadDuration;
            durationMenu.style.display = 'none';
        }
    });

    fileInput.addEventListener('change', () => {
        const file = fileInput.files[0];
        if (file) uploadFile(file);
    });

    const uploadedUrlsKey = 'globalUploadedUrls';
    const urlLimit = 10;

    let savedUrls = GM_getValue(uploadedUrlsKey, []);

    function updateHistoryList() {
        historyList.innerHTML = savedUrls.map(item => `<div><a href="${item.url}" target="_blank">${item.url}</a><span class="timestamp">${item.timestamp}</span></div>`).join('');
    }

    historyButton.addEventListener('click', () => {
        historyList.style.display = historyList.style.display === 'none' ? 'block' : 'none';
        updateHistoryList();
    });

    clearHistoryButton.addEventListener('click', () => {
        if (confirm("Are you sure you want to clear the URL history? This action cannot be undone.")) {
            savedUrls = [];
            GM_setValue(uploadedUrlsKey, savedUrls);
            updateHistoryList();
        }
    });

    function handleFileDrop(e) {
        e.preventDefault();
        dropZone.classList.remove('dragover');
        const file = e.dataTransfer.files[0];
        if (file) uploadFile(file);
    }

    document.addEventListener('dragover', e => {
        e.preventDefault();
        dropZone.style.display = 'block';
        dropZone.classList.add('dragover');
    });

    document.addEventListener('dragleave', () => dropZone.style.display = 'none');
    dropZone.addEventListener('drop', handleFileDrop);

    async function uploadFile(file) {
        const timestamp = new Date().toLocaleString('en-GB');
        try {
            const formData = new FormData();
            formData.append('reqtype', 'fileupload');
            formData.append('fileToUpload', file);

            const response = await fetch('https://litterbox.catbox.moe/resources/internals/api.php', {
                method: 'POST',
                body: formData
            });

            const url = await response.text();
            const newEntry = { url, timestamp };
            savedUrls.push(newEntry);
            if (savedUrls.length > urlLimit) savedUrls.shift();
            GM_setValue(uploadedUrlsKey, savedUrls);

            urlTextBox.value = url;
            urlTextBox.style.display = 'block';
            copyButton.style.display = 'block';

            updateHistoryList();
        } catch (error) {
            console.error("Upload failed:", error);
        }
    }

    copyButton.addEventListener('click', () => {
        navigator.clipboard.writeText(urlTextBox.value).then(() => alert('Copied to clipboard!'));
    });

})();