您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Generate a summary of any webpage, selected text, YouTube video transcript using a local instance of g4f
当前为
// ==UserScript== // @name GPT4Free Page Summarizer (Local Free API) // @version 1.5 // @description Generate a summary of any webpage, selected text, YouTube video transcript using a local instance of g4f // @author SH3LL // @match *://*/* // @grant GM.xmlHttpRequest // @run-at document-end // @namespace http://tampermonkey.net/ // ==/UserScript== function getPageText() { return document.body.innerText; } function getSelectedText() { return window.getSelection().toString(); } function summarizePage(textToSummarize, language, maxLines, callback) { const apiUrl = 'http://localhost:1337/v1/chat/completions'; const prompt = `Summarize the following text in ${language} in max ${maxLines} lines, in a clear, concise and text organised in paragraphs. Each paragraph is logally separated by others by <br> and a title (titles are inside <b></b>). Before each title (in the same line of the title) there si an emoji contextual to the paragraph topic. Don't add any other sentence like "Here is the summary", write directly the summary itself. Here is the text: ${textToSummarize}`; 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 getYouTubeTranscript() { return new Promise((resolve, reject) => { const TRANSCRIPT_TARGET_ID = "engagement-panel-searchable-transcript"; const VISIBILITY_EXPANDED = "ENGAGEMENT_PANEL_VISIBILITY_EXPANDED"; const VISIBILITY_HIDDEN = "ENGAGEMENT_PANEL_VISIBILITY_HIDDEN"; const TRANSCRIPT_CONTENT_SELECTOR = `ytd-engagement-panel-section-list-renderer[target-id="${TRANSCRIPT_TARGET_ID}"] #content`; const SEGMENT_SELECTOR = '.segment-text'; let transcriptPanelElement = null; function hideTranscriptPanel() { if (transcriptPanelElement) { transcriptPanelElement.setAttribute("visibility", VISIBILITY_HIDDEN); transcriptPanelElement = null; // Release the reference } } function extractTranscript() { const transcriptPanelContent = document.querySelector(TRANSCRIPT_CONTENT_SELECTOR); if (transcriptPanelContent) { let transcriptText = ""; transcriptPanelContent.querySelectorAll(SEGMENT_SELECTOR).forEach(segment => { transcriptText += segment.textContent.trim() + " "; }); hideTranscriptPanel(); resolve(transcriptText.trim()); } else { reject("Transcript panel content not found."); } } function onTranscriptPanelVisible() { const transcriptContentObserver = new MutationObserver((mutationsList, observer) => { const transcriptPanelContent = document.querySelector(TRANSCRIPT_CONTENT_SELECTOR); if (transcriptPanelContent && transcriptPanelContent.children.length > 0) { extractTranscript(); observer.disconnect(); // Stop observing once extracted } }); const transcriptPanel = document.querySelector(`ytd-engagement-panel-section-list-renderer[target-id="${TRANSCRIPT_TARGET_ID}"]`); const contentTarget = transcriptPanel ? transcriptPanel.querySelector('#content') : null; const config = { childList: true, subtree: true }; // Observe for addition of child nodes if (contentTarget) { transcriptContentObserver.observe(contentTarget, config); } else { reject("Could not find the #content element within the transcript panel."); } } function showTranscriptPanelAndObserveContent() { const transcripts = document.querySelectorAll(`[target-id="${TRANSCRIPT_TARGET_ID}"]`); if (transcripts.length === 1) { transcriptPanelElement = transcripts[0]; // Store panel element transcriptPanelElement.setAttribute("visibility", VISIBILITY_EXPANDED); onTranscriptPanelVisible(); } else { reject('Transcript panel element not found.'); } } const panelObserver = new MutationObserver((mutationsList, observer) => { const transcriptPanelContainer = document.querySelector(`ytd-engagement-panel-section-list-renderer[target-id="${TRANSCRIPT_TARGET_ID}"]`); if (transcriptPanelContainer) { showTranscriptPanelAndObserveContent(); observer.disconnect(); // Stop observing once found } }); // Start observing for the transcript panel container const targetNode = document.body; const config = { childList: true, subtree: true }; panelObserver.observe(targetNode, config); // Set a timeout in case the transcript panel doesn't load setTimeout(() => { if (!transcriptPanelElement && !document.querySelector(TRANSCRIPT_CONTENT_SELECTOR)) { panelObserver.disconnect(); // Ensure observer is stopped reject("Timeout: Could not find transcript panel."); } }, 10000); // Adjust timeout as needed }); } (function() { 'use strict'; // GET LANGUAGE PAGE const browserLanguage = navigator.language; let selectedLanguage; try { const languageNames = new Intl.DisplayNames([browserLanguage], { type: 'language' }); selectedLanguage = languageNames.of(browserLanguage); // Fallback to the raw language code if Intl.DisplayNames fails or returns undefined if (!selectedLanguage) { selectedLanguage = browserLanguage; } } catch (error) { console.error("Error initializing Intl.DisplayNames:", error); selectedLanguage = browserLanguage; // Fallback to raw language code } // GET YOUTUBE TRANSCRIPT let ytTranscript = null; const isYouTubeVideoPage = window.location.hostname.includes("youtube.com") && window.location.pathname === "/watch"; if (isYouTubeVideoPage) { getYouTubeTranscript() .then(transcript => { ytTranscript = transcript; console.log("YOUTUBE Transcript:", transcript); }) .catch(error => { console.error("Error getting YOUTUBE transcript:", error); }); } // CREATE DOM CONTAINER const shadowHost = document.createElement('div'); document.body.appendChild(shadowHost); const shadowRoot = shadowHost.attachShadow({ mode: 'open' }); // CREATE SIDEBAR DOM 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 = '999999'; // Increased to ensure it's above all elements 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'; shadowRoot.appendChild(sidebar); // CREATE BUTTON HIDE/SHOW SIDEBAR const toggleButton = document.createElement('button'); toggleButton.textContent = '<'; toggleButton.style.position = 'fixed'; toggleButton.style.right = '0'; toggleButton.style.top = '20px'; toggleButton.style.backgroundColor = '#FFC107'; // Mustard yellow toggleButton.style.color = '#000000'; // White text toggleButton.style.border = 'none'; toggleButton.style.padding = '10px'; toggleButton.style.cursor = 'pointer'; toggleButton.style.zIndex = '1000000'; // Higher than sidebar toggleButton.style.fontSize = '14px'; toggleButton.style.transition = 'right 0.3s ease'; shadowRoot.appendChild(toggleButton); // CREATE CONTAINER FOR BUTTON AND SELECTOR const buttonContainer = document.createElement('div'); buttonContainer.style.display = 'flex'; buttonContainer.style.gap = '10px'; buttonContainer.style.alignItems = 'center'; sidebar.appendChild(buttonContainer); // CREATE SUMMARIZE BUTTON const summarizeButton = document.createElement('button'); if (isYouTubeVideoPage && ytTranscript!==null) { summarizeButton.textContent = 'Summarize YT'; }else{ summarizeButton.textContent = 'Summarize'; } summarizeButton.style.backgroundColor = '#333333'; summarizeButton.style.color = '#ffffff'; 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 = '#4d4d4d'; summarizeButton.onmouseout = () => summarizeButton.style.backgroundColor = '#333333'; buttonContainer.appendChild(summarizeButton); // Create the line number selector const linesSelector = document.createElement('select'); linesSelector.style.backgroundColor = '#333333'; linesSelector.style.color = '#ffffff'; linesSelector.style.border = 'none'; linesSelector.style.padding = '10px'; linesSelector.style.fontSize = '14px'; linesSelector.style.cursor = 'pointer'; const lineOptions = [5, 10, 15, 20, 25, 30, 50, 100]; lineOptions.forEach(lines => { const option = document.createElement('option'); option.value = lines; option.textContent = `${lines} lines`; if (lines === 5) option.selected = true; 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)'; sidebar.appendChild(summaryContainer); // Variable to track if text is selected let isTextSelected = false; // Function to update the button text based on text selection function updateButtonText() { const selectedText = getSelectedText(); if (selectedText) { summarizeButton.textContent = `Summarize [${selectedText.substring(0, 2).trim().replace(/^\n+/, '').trim()}..]`; summarizeButton.style.fontSize = "14px"; isTextSelected = true; } else if (isYouTubeVideoPage && ytTranscript!==null) { summarizeButton.textContent = 'Summarize YT'; summarizeButton.style.fontSize = "14px"; isTextSelected = false; } else { summarizeButton.textContent = 'Summarize'; summarizeButton.style.fontSize = "14px"; isTextSelected = false; } } // Listen for text selection changes on the document document.addEventListener('mouseup', updateButtonText); document.addEventListener('mousedown', () => { setTimeout(updateButtonText, 100); }); // 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; }); // Listener for the summarize button summarizeButton.addEventListener('click', function() { summarizeButton.disabled = true; summarizeButton.textContent = 'Loading...'; statusDisplay.style.color = '#888888'; statusDisplay.textContent = `Status: Requesting... | Lang: ${selectedLanguage}`; summaryContainer.style.display = 'none'; summaryContainer.style.whiteSpace = 'pre-line'; let textToSummarize = ''; if (isYouTubeVideoPage && !isTextSelected && ytTranscript) { textToSummarize = ytTranscript; console.log("Summarizing YouTube transcript..."); } else if (isTextSelected) { textToSummarize = getSelectedText(); console.log("Summarizing selected text..."); } else { textToSummarize = getPageText(); console.log("Summarizing page text..."); } const maxLines = parseInt(linesSelector.value, 10); summarizePage(textToSummarize, selectedLanguage, maxLines, function(error, summary, statusCode) { summarizeButton.disabled = false; updateButtonText(); if (error) { summaryContainer.textContent = 'Error: ' + error; summaryContainer.style.color = '#ff4444'; statusDisplay.textContent = `Status: Failed${statusCode ? ` (${statusCode})` : ''} | Lang: ${selectedLanguage}`; statusDisplay.style.color = '#ff4444'; } else { summaryContainer.innerHTML = summary; summaryContainer.style.color = '#ffffff'; statusDisplay.textContent = `Status: Success (${statusCode}) | Lang: ${selectedLanguage}`; statusDisplay.style.color = '#00ff00'; } summaryContainer.style.display = 'block'; }); }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址