GPT4Free Page Summarizer (Local Free API)

Generate a summary of any webpage using a local instance of g4f

Від 10.04.2025. Дивіться остання версія.

// ==UserScript==
// @name         GPT4Free Page Summarizer (Local Free API)
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Generate a summary of any webpage using a local instance of g4f
// @author       SH3LL
// @match        *://*/*
// @grant        GM.xmlHttpRequest
// ==/UserScript==

function getPageText() {
    return document.body.innerText;
}

function summarizePage(pageText, language, maxLines, callback) {
    const apiUrl = 'http://localhost:1337/v1/chat/completions';
    const prompt = `Summarize the page in ${language} in max ${maxLines} lines, in a clear, concise and text organised in paragraphs.
                    Don't add any other sentence like "Here is the summary", write directly the summary itself.
                    Here is the text of the page: ${pageText}`;
    const data = {
        messages: [{
            role: 'user',
            content: prompt
        }],
        model: 'DeepSeek-V3',
        provider: 'Blackbox'
    };

    GM.xmlHttpRequest({
        method: 'POST',
        url: apiUrl,
        headers: {
            'Content-Type': 'application/json'
        },
        data: JSON.stringify(data),
        onload: function(response) {
            if (response.status >= 200 && response.status < 300) {
                try {
                    const jsonResponse = JSON.parse(response.responseText);
                    if (jsonResponse.choices && jsonResponse.choices[0].message && jsonResponse.choices[0].message.content) {
                        callback(null, jsonResponse.choices[0].message.content, response.status);
                    } else {
                        callback('Unexpected format of the API response.', null, response.status);
                    }
                } catch (error) {
                    callback('Error parsing the API response: ' + error, null, response.status);
                }
            } else {
                callback('Error in the API call: ' + response.status + ' ' + response.statusText, null, response.status);
            }
        },
        onerror: function(error) {
            callback('Network error during the API call: ' + error, null, null);
        }
    });
}

(function() {
    'use strict';

    const languageNames = new Intl.DisplayNames(['it'], { type: 'language' });
    const browserLanguage = navigator.language;
    const selectedLanguage = languageNames.of(browserLanguage);

    // Create the sidebar container
    const sidebar = document.createElement('div');
    sidebar.style.position = 'fixed';
    sidebar.style.right = '-300px';
    sidebar.style.top = '0';
    sidebar.style.width = '300px';
    sidebar.style.height = '100vh';
    sidebar.style.backgroundColor = '#000000';
    sidebar.style.color = '#ffffff';
    sidebar.style.padding = '20px';
    sidebar.style.zIndex = '9999';
    sidebar.style.fontFamily = 'Arial, sans-serif';
    sidebar.style.boxSizing = 'border-box';
    sidebar.style.display = 'flex';
    sidebar.style.flexDirection = 'column';
    sidebar.style.gap = '10px';
    sidebar.style.transition = 'right 0.3s ease';
    document.body.appendChild(sidebar);

    // Create the button to show/hide the sidebar
    const toggleButton = document.createElement('button');
    toggleButton.textContent = '>';
    toggleButton.style.position = 'fixed';
    toggleButton.style.right = '0';
    toggleButton.style.top = '20px';
    toggleButton.style.backgroundColor = '#000000';
    toggleButton.style.color = '#ffffff';
    toggleButton.style.border = 'none';
    toggleButton.style.padding = '10px';
    toggleButton.style.cursor = 'pointer';
    toggleButton.style.zIndex = '10000';
    toggleButton.style.fontSize = '14px';
    toggleButton.style.transition = 'right 0.3s ease';
    document.body.appendChild(toggleButton);

    // Create the container for the button and selector
    const buttonContainer = document.createElement('div');
    buttonContainer.style.display = 'flex';
    buttonContainer.style.gap = '10px';
    buttonContainer.style.alignItems = 'center';
    sidebar.appendChild(buttonContainer);

    // Create the summarize button
    const summarizeButton = document.createElement('button');
    summarizeButton.textContent = 'Summarize';
    summarizeButton.style.backgroundColor = '#ffffff';
    summarizeButton.style.color = '#000000';
    summarizeButton.style.border = 'none';
    summarizeButton.style.padding = '10px 20px';
    summarizeButton.style.cursor = 'pointer';
    summarizeButton.style.fontSize = '14px';
    summarizeButton.style.flex = '1';
    summarizeButton.style.transition = 'background-color 0.3s';
    summarizeButton.onmouseover = () => summarizeButton.style.backgroundColor = '#e0e0e0';
    summarizeButton.onmouseout = () => summarizeButton.style.backgroundColor = '#ffffff';
    buttonContainer.appendChild(summarizeButton);

    // Create the selector for the number of lines
    const linesSelector = document.createElement('select');
    linesSelector.style.backgroundColor = '#ffffff';
    linesSelector.style.color = '#000000';
    linesSelector.style.border = 'none';
    linesSelector.style.padding = '10px';
    linesSelector.style.fontSize = '14px';
    linesSelector.style.cursor = 'pointer';
    const lineOptions = [10, 15, 20, 25, 30, 50, 100];
    lineOptions.forEach(lines => {
        const option = document.createElement('option');
        option.value = lines;
        option.textContent = `${lines} lines`;
        if (lines === 10) option.selected = true; // Default to 10 lines
        linesSelector.appendChild(option);
    });
    buttonContainer.appendChild(linesSelector);

    // Create the API status display with detected language
    const statusDisplay = document.createElement('div');
    statusDisplay.style.fontSize = '12px';
    statusDisplay.style.color = '#888888';
    statusDisplay.textContent = `Status: Idle | Lang: ${selectedLanguage}`;
    sidebar.appendChild(statusDisplay);

    // Create the summary container
    const summaryContainer = document.createElement('div');
    summaryContainer.style.fontSize = '14px';
    summaryContainer.style.lineHeight = '1.5';
    summaryContainer.style.display = 'none';
    summaryContainer.style.overflowY = 'auto';
    summaryContainer.style.maxHeight = 'calc(100vh - 130px)'; // Adjusted space for the selector
    sidebar.appendChild(summaryContainer);

    // Handle sidebar visibility
    let isSidebarVisible = false;
    toggleButton.addEventListener('click', function() {
        if (isSidebarVisible) {
            sidebar.style.right = '-300px';
            toggleButton.style.right = '0';
            toggleButton.textContent = '>';
        } else {
            sidebar.style.right = '0';
            toggleButton.style.right = '300px';
            toggleButton.textContent = '<';
        }
        isSidebarVisible = !isSidebarVisible;
    });

    // Event listener for the summarize button
    summarizeButton.addEventListener('click', function() {
        summarizeButton.disabled = true;
        summarizeButton.textContent = 'Loading...';
        statusDisplay.textContent = `Status: Requesting... | Lang: ${selectedLanguage}`;
        summaryContainer.style.display = 'none';

        const pageText = getPageText();
        const maxLines = parseInt(linesSelector.value, 10); // Get the value from the selector
        summarizePage(pageText, selectedLanguage, maxLines, function(error, summary, statusCode) {
            summarizeButton.disabled = false;
            summarizeButton.textContent = 'Summarize Page';
            if (error) {
                summaryContainer.textContent = 'Error: ' + error;
                summaryContainer.style.color = '#ff4444';
                statusDisplay.textContent = `Status: Failed${statusCode ? ` (${statusCode})` : ''} | Lang: ${selectedLanguage}`;
                statusDisplay.style.color = '#ff4444';
            } else {
                summaryContainer.textContent = summary;
                summaryContainer.style.color = '#ffffff';
                statusDisplay.textContent = `Status: Success (${statusCode}) | Lang: ${selectedLanguage}`;
                statusDisplay.style.color = '#00ff00';
            }
            summaryContainer.style.display = 'block';
        });
    });
})();