您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
修正 Imgur 在 PTT 上的問題
当前为
// ==UserScript== // @name PTT Imgur Fix // @description 修正 Imgur 在 PTT 上的問題 // @namespace eight04.blogspot.com // @match https://www.ptt.cc/bbs/*.html // @match https://www.ptt.cc/man/*.html // @version 0.7.0 // @author eight // @homepage https://github.com/eight04/ptt-imgur-fix // @supportURL https://github.com/eight04/ptt-imgur-fix/issues // @license MIT // @compatible firefox Tampermonkey, Violentmonkey, Greasemonkey 4.11+ // @compatible chrome Tampermonkey, Violentmonkey // @run-at document-start // @grant GM_getValue // @grant GM.getValue // @grant GM_setValue // @grant GM.setValue // @grant GM_deleteValue // @grant GM.deleteValue // @grant GM_addValueChangeListener // @grant GM_registerMenuCommand // @grant GM.registerMenuCommand // @grant GM_xmlhttpRequest // @grant GM.xmlHttpRequest // @require https://greasyfork.org/scripts/371339-gm-webextpref/code/GM_webextPref.js?version=961539 // @connect imgur.com // ==/UserScript== /* global GM_webextPref */ const request = typeof GM_xmlhttpRequest === "function" ? GM_xmlhttpRequest : GM.xmlHttpRequest; const pref = GM_webextPref({ default: { embedYoutube: true, youtubeParameters: "", embedImage: true, embedAlbum: false, albumMaxSize: 5, imgurVideo: false, lazyLoad: true }, body: [ { key: "embedImage", label: "Embed image", type: "checkbox", }, { key: "embedAlbum", label: "Embed imgur album. The script would request imgur.com for album info", type: "checkbox", children: [ { key: "albumMaxSize", label: "Maximum number of images to load for an album", type: "number" } ] }, { key: "imgurVideo", label: "Embed imgur video instead of GIF. Reduce file size", type: "checkbox" }, { key: "embedYoutube", label: "Embed youtube video", type: "checkbox", children: [ { key: "youtubeParameters", label: "Youtube player parameters (e.g. rel=0&loop=1)", type: "text", default: "" } ] }, { key: "lazyLoad", label: "Don't load images until scrolled into view", type: "checkbox" } ], navbar: false }); document.addEventListener("beforescriptexecute", e => { var url = new URL(e.target.src, location.href); if (url.hostname.endsWith("imgur.com")) { e.preventDefault(); } }); Promise.all([ pref.ready(), domReady() ]) .then(embedLinks) .catch(console.error); function domReady() { return new Promise(resolve => { if (document.readyState !== "loading") { resolve(); return; } document.addEventListener("DOMContentLoaded", resolve, {once: true}); }); } function embedLinks() { // remove old .richcontent var rich = document.querySelectorAll("#main-content .richcontent"); for (var node of rich) { node.parentNode.removeChild(node); } // embed links var links = document.querySelectorAll("#main-content a"), processed = new Set; for (var link of links) { if (processed.has(link) || !getLinkInfo(link).embedable) { continue; } var [links_, lineEnd] = findLinksInSameLine(link); links_.forEach(l => processed.add(l)); createRichContent(links_, lineEnd); } } function findLinksInSameLine(node) { var links = []; while (node) { if (node.nodeName == "A") { links.push(node); node = node.nextSibling || node.parentNode.nextSibling; continue; } if (node.nodeType == Node.TEXT_NODE && node.nodeValue.includes("\n")) { return [links, findLineEnd(node)]; } if (node.childNodes.length) { node = node.childNodes[0]; continue; } if (node.nextSibling) { node = node.nextSibling; continue; } if (node.parentNode.id != "main-content") { node = node.parentNode.nextSibling; continue; } throw new Error("Invalid article, missing new line?"); } } function findLineEnd(text) { var index = text.nodeValue.indexOf("\n"); if (index == text.nodeValue.length - 1) { while (text.parentNode.id != "main-content") { text = text.parentNode; } return text; } var pre = document.createTextNode(""); pre.nodeValue = text.nodeValue.slice(0, index + 1); text.nodeValue = text.nodeValue.slice(index + 1); text.parentNode.insertBefore(pre, text); return pre; } // insert richcontent brefore ref.nextSibling function createRichContent(links, ref) { // create our rich content for (var link of links) { var linkInfo = getLinkInfo(link); if (!linkInfo.embedable) { continue; } var richContent = document.createElement("div"); richContent.className = "richcontent ptt-imgur-fix"; const embed = createEmbed(linkInfo, richContent); if (typeof embed === "string") { richContent.innerHTML = embed; } else if (embed) { richContent.appendChild(embed); } const lazyTarget = richContent.querySelector("[data-src]"); if (lazyTarget) { setupLazyLoad(lazyTarget, !pref.get("lazyLoad")); } ref.parentNode.insertBefore(richContent, ref.nextSibling); ref = richContent; } } function setupLazyLoad(target, forceLoad = false) { if (forceLoad) { load(); return; } const observer = new IntersectionObserver(entries => { for (const entry of entries) { if (entry.isIntersecting) { load(); } else { unload(); } } }); observer.observe(target); function load() { target.src = target.dataset.src; target.dispatchEvent(new CustomEvent("lazyload")); } function unload() { if (target.videoHeight || target.naturalHeight) { const {offsetWidth, offsetHeight} = target; target.style.width = offsetWidth + "px"; target.style.height = offsetHeight + "px"; } target.src = ""; } } function getLinkInfo(link) { return getUrlInfo(link.href); } function getUrlInfo(url) { var match; if ((match = url.match(/\/\/(?:[im]\.)?imgur\.com\/([a-z0-9]{2,})(\.[a-z0-9]{3,4})?/i)) && match[1] != "gallery") { return { type: "imgur", id: match[1], url: url, embedable: pref.get("embedImage"), extension: match[2] && match[2].toLowerCase() }; } if ((match = url.match(/\/\/(?:[im]\.)?imgur\.com\/(?:a|gallery)\/([a-z0-9]{2,})/i))) { return { type: "imgur-album", id: match[1], url: url, embedable: pref.get("embedAlbum") }; } if ((match = url.match(/\/\/www\.youtube\.com\/watch?.*?v=([a-z0-9_-]{9,12})/i)) || (match = url.match(/\/\/(?:youtu\.be|www\.youtube\.com\/embed)\/([a-z0-9_-]{9,12})/i))) { return { type: "youtube", id: match[1], url: url, embedable: pref.get("embedYoutube") }; } if ((match = url.match(/\/\/pbs\.twimg\.com\/media\/([a-z0-9_-]+\.(?:jpg|png))/i))) { return { type: "twitter", id: match[1], url: url, embedable: pref.get("embedImage") }; } if ((match = url.match(/\/\/pbs\.twimg\.com\/media\/([a-z0-9_-]+)\?.*format=([\w]+)/i))) { return { type: "twitter", id: `${match[1]}.${match[2]}`, url: url, embedable: pref.get("embedImage") }; } if (/^[^?#]+\.(?:jpg|png|gif|jpeg)(?:$|[?#])/i.test(url)) { return { type: "image", id: null, url: url, embedable: pref.get("embedImage") }; } return { type: "url", id: null, url: url, embedable: false }; } function createEmbed(info, container) { if (info.type == "imgur") { let extension = info.extension || ".jpg"; if (extension === ".gif" && pref.get("imgurVideo")) { extension = ".mp4"; } const url = `//i.imgur.com/${info.id}${extension}`; if (extension !== ".mp4") { return `<img referrerpolicy="no-referrer" data-src="${url}">`; } const video = document.createElement("video"); video.loop = true; video.autoplay = true; video.controls = true; video.dataset.src = ""; video.addEventListener("lazyload", () => { fetch(url, { referrerPolicy: "no-referrer" }) .then(r => r.blob()) .then(response => { const finalUrl = URL.createObjectURL(response); video.dataset.src = finalUrl; video.src = finalUrl; }); }, {once: true}); return video; } if (info.type == "youtube") { return `<div class="resize-container"><div class="resize-content"><iframe class="youtube-player" type="text/html" data-src="//www.youtube.com/embed/${info.id}?${pref.get("youtubeParameters")}" frameborder="0" allowfullscreen></iframe></div></div>`; } if (info.type == "image") { return `<img referrerpolicy="no-referrer" data-src="${info.url}">`; } if (info.type == "twitter") { const image = new Image; const urls = [ `//pbs.twimg.com/media/${info.id}:orig`, `//pbs.twimg.com/media/${info.id.replace(/\.jpg\b/, ".png")}:orig`, `//pbs.twimg.com/media/${info.id}:large`, `//pbs.twimg.com/media/${info.id}`, ]; image.dataset.src = urls.shift(); image.addEventListener("error", function onerror() { if (!image.currentSrc || !urls.length) { // ignore empty image error return; } const newUrl = urls.shift(); image.dataset.src = newUrl; image.src = newUrl; }); return image; } if (info.type == "imgur-album") { container.textContent = "Loading album..."; request({ method: "GET", url: `https://api.imgur.com/post/v1/albums/${info.id}?client_id=546c25a59c58ad7&include=media`, responseType: "json", onload(response) { if (response.status < 200 || response.status >= 300) { container.textContent = `${response.status} ${response.statusText}`; return; } container.textContent = ""; const urls = response.response.media.map(m => m.url); let i = 0; const loadImages = (count = Infinity) => { let html = ""; for (; i < urls.length && count--; i++) { html += `<div class="richcontent"><img referrerpolicy="no-referrer" src="${urls[i]}"></div>`; } container.insertAdjacentHTML("beforeend", html); }; loadImages(pref.get("albumMaxSize")); if (i < urls.length) { const button = document.createElement("button"); button.textContent = `Load all images (${urls.length - i} more)`; button.addEventListener('click', () => { button.remove(); loadImages(); }); container.appendChild(button); } } }); return; } throw new Error(`Invalid type: ${info.type}`); }