diff --git a/.github/workflows/json-validator.yml b/.github/workflows/json-validator.yml index a726236d040..01911cc1730 100644 --- a/.github/workflows/json-validator.yml +++ b/.github/workflows/json-validator.yml @@ -13,9 +13,9 @@ jobs: - uses: actions/checkout@v4 - name: json-yaml-validate - uses: GrantBirki/json-yaml-validate@v3.0.0 + uses: GrantBirki/json-yaml-validate@v5 id: json-yaml-validate with: json_schema: Pagetual/pagetual.schema.json files: | - Pagetual/pagetualRules.json \ No newline at end of file + Pagetual/pagetualRules.json diff --git a/BingBgForBaidu/BingBgForBaidu.user.js b/BingBgForBaidu/BingBgForBaidu.user.js index 6c86b1050dd..ba0fa832396 100644 --- a/BingBgForBaidu/BingBgForBaidu.user.js +++ b/BingBgForBaidu/BingBgForBaidu.user.js @@ -2,7 +2,7 @@ // @name 百Bing图 // @name:en BingBgForBaidu // @namespace hoothin -// @version 2.3.44 +// @version 2.3.45 // @description 给百度首页换上Bing的背景图,并添加背景图链接与日历组件 // @description:en Just change the background image of baidu.com to bing.com // @author hoothin @@ -124,7 +124,10 @@ } }; var skinContainer=document.getElementsByTagName("body")[0]; - GM_addStyle(".s-news-rank-content{max-height: 180px; width: 99%; overflow-y: auto; overflow-x: hidden;}.s-top-right .ai-entry-right-nologin,.s-top-right .operate-wrapper-nologin{right:362px;}.hot-refresh{padding-bottom:7px;}.hot-title>div,.hot-refresh{border-radius: 3px 3px 0 0}.s-hotsearch-title>a,.s-hotsearch-title>a>div{padding: 5px;background-color: #f0f8ff95;border-radius: 5px;}.s-hotsearch-content{position: absolute; background-color: #f0f8ff95; border-radius: 5px;padding: 5px;}.s_ipt{margin:0!important;}.s_ipt_wr{border-radius: 10px 4px 4px 10px;border-radius: 10px 0 0 10px;background: #fff!important;}#qrcodeCon{display:none}body{position:fixed;_position:absolute;top:0;left:0;height:100%;width:100%;min-width:1000px;z-index:-10;background-position:center 0;background-repeat:no-repeat;background-size:cover;-webkit-background-size:cover;-o-background-size:cover;zoom:1;}"); + if (!document.querySelector(".aigc-skin-bg,#s_mancard_main")) { + GM_addStyle(".s-news-rank-content{max-height: 180px; width: 99%; overflow-y: auto; overflow-x: hidden;}.s-top-right .ai-entry-right-nologin,.s-top-right .operate-wrapper-nologin{right:362px;}.hot-refresh{padding-bottom:7px;}.hot-title>div,.hot-refresh{border-radius: 3px 3px 0 0}.s-hotsearch-title>a,.s-hotsearch-title>a>div{padding: 5px;background-color: #f0f8ff95;border-radius: 5px;}.s-hotsearch-content{position: absolute; background-color: #f0f8ff95; border-radius: 5px;padding: 5px;}"); + } + GM_addStyle(".s_ipt{margin:0!important;}.s_ipt_wr{border-radius: 10px 4px 4px 10px;border-radius: 10px 0 0 10px;background: #fff!important;}#qrcodeCon{display:none}body{position:fixed;_position:absolute;top:0;left:0;height:100%;width:100%;min-width:1000px;z-index:-10;background-position:center 0;background-repeat:no-repeat;background-size:cover;-webkit-background-size:cover;-o-background-size:cover;zoom:1;}"); var inputsu=document.querySelector("input#su"); var clickHandler=e=>{ if(skinContainer)skinContainer.style.backgroundImage=""; diff --git a/Pagetual/README.md b/Pagetual/README.md index 7cb2e779d7d..40854710e73 100644 --- a/Pagetual/README.md +++ b/Pagetual/README.md @@ -1,4 +1,4 @@ -[☯️](https://greasyfork.org/scripts/438684 "Install from greasyfork")東方永頁機 [v.1.9.37.129](https://hoothin.github.io/UserScripts/Pagetual/pagetual.user.js "Latest version") +[☯️](https://greasyfork.org/scripts/438684 "Install from greasyfork")東方永頁機 [v.1.9.37.131](https://hoothin.github.io/UserScripts/Pagetual/pagetual.user.js "Latest version") == *Pagetual - Perpetual pages. Auto loading paginated web pages for 90% of all web sites !* diff --git a/Pagetual/pagetual.user.js b/Pagetual/pagetual.user.js index 8fce3cc256a..ae4b3ad94f3 100644 --- a/Pagetual/pagetual.user.js +++ b/Pagetual/pagetual.user.js @@ -31,7 +31,7 @@ // @name:da Pagetual // @name:fr-CA Pagetual // @namespace hoothin -// @version 1.9.37.129 +// @version 1.9.37.132 // @description Perpetual pages - powerful auto-pager script. Auto fetching next paginated web pages and inserting into current page for infinite scroll. Support thousands of web sites without any rule. // @description:zh-CN 终极自动翻页 - 加载并拼接下一分页内容至当前页尾,智能适配任意网页 // @description:zh-TW 終極自動翻頁 - 加載並拼接下一分頁內容至當前頁尾,智能適配任意網頁 @@ -4170,6 +4170,88 @@ } function requestWithFetch(f, onFetchError) { + function getHeaderValue(headers, name) { + if (!headers || !name) return ""; + let lowerName = String(name).toLowerCase(); + if (typeof Headers !== "undefined" && headers instanceof Headers) { + return headers.get(lowerName) || ""; + } + if (typeof headers === "object") { + for (let key in headers) { + if (Object.prototype.hasOwnProperty.call(headers, key) && String(key).toLowerCase() === lowerName) { + return headers[key] || ""; + } + } + } + return ""; + } + function extractCharsetFromContentType(contentType) { + if (!contentType || typeof contentType !== "string") return ""; + let match = contentType.match(/charset\s*=\s*["']?([^;"'\s]+)/i); + return match && match[1] ? match[1].trim() : ""; + } + function decodeArrayBufferByCharset(buffer, preferredCharset) { + let bytes = new Uint8Array(buffer); + let decoderList = []; + let normalize = label => String(label || "").trim().toLowerCase(); + let addAlias = (name, aliases) => { + if (aliases.indexOf(normalizedPreferred) !== -1) { + pushDecoder(name); + for (let i = 0; i < aliases.length; i++) { + pushDecoder(aliases[i]); + } + } + }; + let pushDecoder = label => { + let raw = String(label || "").trim(); + if (raw && decoderList.indexOf(raw) === -1) { + decoderList.push(raw); + } + }; + let normalizedPreferred = normalize(preferredCharset).replace(/["']/g, ""); + pushDecoder(preferredCharset); + pushDecoder(normalizedPreferred); + pushDecoder(normalizedPreferred.replace(/_/g, "-")); + pushDecoder(normalizedPreferred.replace(/-/g, "_")); + addAlias("shift_jis", ["shiftjis", "shift-jis", "sjis", "ms_kanji", "windows-31j", "cp932", "ms932"]); + addAlias("euc-jp", ["eucjp"]); + addAlias("iso-2022-jp", ["iso2022jp", "jis"]); + addAlias("gb18030", ["gbk", "gb2312", "x-gbk", "cp936", "ms936", "windows-936"]); + addAlias("big5", ["big-5", "cn-big5", "x-x-big5"]); + addAlias("euc-kr", ["euckr", "ks_c_5601-1987", "ksc5601", "windows-949", "cp949"]); + addAlias("windows-1251", ["cp1251"]); + addAlias("windows-1252", ["cp1252", "iso-8859-1", "latin1", "latin-1"]); + pushDecoder("utf-8"); + for (let i = 0; i < decoderList.length; i++) { + try { + return new TextDecoder(decoderList[i]).decode(bytes); + } catch (e) {} + } + try { + return new TextDecoder().decode(bytes); + } catch (e) { + return ""; + } + } + function detectCharsetFromHtmlHead(buffer) { + if (!buffer || !buffer.byteLength) return ""; + let scanLen = Math.min(buffer.byteLength, 16384); + let bytes = new Uint8Array(buffer, 0, scanLen); + let ascii = ""; + for (let i = 0; i < bytes.length; i++) { + let code = bytes[i]; + ascii += code < 128 ? String.fromCharCode(code) : " "; + } + let charsetMatch = ascii.match(/]*charset\s*=\s*["']?\s*([a-zA-Z0-9._-]+)/i); + if (!charsetMatch) { + charsetMatch = ascii.match(/]*content\s*=\s*["'][^"']*charset\s*=\s*([a-zA-Z0-9._-]+)/i); + } + return charsetMatch && charsetMatch[1] ? charsetMatch[1].trim() : ""; + } + function isUtf8Charset(label) { + let normalized = String(label || "").trim().toLowerCase().replace(/_/g, "-"); + return !normalized || normalized === "utf-8" || normalized === "utf8"; + } if (!f || !f.url || typeof fetch === "undefined") { if (onFetchError) { onFetchError({error: "Fetch not available"}); @@ -4196,7 +4278,27 @@ } fetch(f.url, options).then(async response => { if (timeoutId) clearTimeout(timeoutId); - let text = await response.text(); + let responseCharset = extractCharsetFromContentType(response.headers.get("content-type") || ""); + let overrideCharset = extractCharsetFromContentType(f.overrideMimeType || ""); + let requestCharset = extractCharsetFromContentType(getHeaderValue(options.headers, "content-type")); + let text; + let charsetFromHeaders = responseCharset || overrideCharset || requestCharset; + if (charsetFromHeaders) { + if (isUtf8Charset(charsetFromHeaders)) { + text = await response.text(); + } else { + text = decodeArrayBufferByCharset(await response.arrayBuffer(), charsetFromHeaders); + } + } else { + let rawBuffer = await response.arrayBuffer(); + let metaCharset = detectCharsetFromHtmlHead(rawBuffer); + let targetCharset = metaCharset || charset || "utf-8"; + if (isUtf8Charset(targetCharset)) { + text = new TextDecoder("utf-8").decode(new Uint8Array(rawBuffer)); + } else { + text = decodeArrayBufferByCharset(rawBuffer, targetCharset); + } + } let headers = ""; response.headers.forEach((value, key) => { headers += key + ": " + value + "\r\n"; @@ -4408,8 +4510,10 @@ "https://github.com/hoothin/UserScripts/tree/master/Pagetual", "https://hoothin.github.io/UserScripts/Pagetual/"]; const firstRunPage = "https://pagetual.hoothin.com/firstRun"; + const wedataRulesUrl = "http://wedata.net/databases/AutoPagerize/items_all.json"; + const wedataMirrorRulesUrl = "https://hoothin.github.io/UserScripts/Pagetual/items_all.json"; const guidePage = /^https?:\/\/.*pagetual.*rule\.html/i; - const ruleImportUrlReg = /greasyfork\.org\/.*scripts\/438684(\-[^\/]*)?(\/discussions|\/?$|\/feedback)|github\.com\/hoothin\/UserScripts\/(tree\/master\/Pagetual|issues)|^https:\/\/pagetual\.hoothin\.com\/.*firstRun\.html/i; + const ruleImportUrlReg = /greasyfork\.org\/.*scripts\/438684(\-[^\/]*)?(\/discussions|\/?$|\/feedback)|github\.com\/hoothin\/UserScripts\/(tree\/master\/Pagetual|issues)|^https:\/\/pagetual\.hoothin\.com\/.*first(Run|-run)\.html/i; const allOfBody = "body>*"; const mainSel = ["article,.article","[role=main],main,.main,#main","#results"]; const nextTextReg1 = new RegExp("\u005e\u7ffb\u003f\u005b\u4e0b\u540e\u5f8c\u6b21\u005d\u005b\u4e00\u30fc\u2500\u0031\u005d\u003f\u005b\u9875\u9801\u5f20\u5f35\u005d\u007c\u005e\u006e\u0065\u0078\u0074\u005b\u005c\u0073\u005f\u002d\u005d\u003f\u0070\u0061\u0067\u0065\u005c\u0073\u002a\u005b\u203a\u003e\u2192\u00bb\u005d\u003f\u0024\u007c\u6b21\u306e\u30da\u30fc\u30b8\u007c\u005e\u6b21\u3078\u0024\u007c\u0412\u043f\u0435\u0440\u0435\u0434\u007c\u005e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435", "i"); @@ -9282,7 +9386,7 @@ } const picker = new Picker(); - var editor, editorChanged = false, customRulesInput; + var editor, editorChanged = false, customRulesInput, wedata2githubInputRef; function createEdit() { var options = { mode: 'code', @@ -10144,6 +10248,7 @@ let arrowToScrollInput = createCheckbox(i18n("arrowToScroll"), rulesData.arrowToScroll); let contentVisibilityInput = createCheckbox(i18n("contentVisibility"), rulesData.contentVisibility); let wedata2githubInput = createCheckbox(i18n("wedata2github"), rulesData.wedata2github); + wedata2githubInputRef = wedata2githubInput; let customFirstInput = createCheckbox(i18n("customFirst"), rulesData.customFirst); let lastPageTipsInput = createCheckbox(i18n("lastPageTips"), rulesData.lastPageTips); let updateNotificationInput = createCheckbox(i18n("updateNotification"), rulesData.updateNotification != false); @@ -10200,13 +10305,13 @@ }, (rule, err) => { if (rule.id == 1) { showTips(`Failed to update wedata rules! Try to switch to wedata mirror!`); - wedata2githubInput.scrollIntoView({ behavior: "smooth" }); + wedata2githubInput.scrollIntoView({ behavior: "smooth", block: "center", inline: "center" }); } else { showTips(`Failed to update ${rule.url} rules!`); } debug(err); updateFail = true; - }); + }, false, true); }; let customRulesTitle = document.createElement("h2"); setHTML(customRulesTitle, i18n("customRules", /tree/.test(location.href) ? location.href.replace('tree', 'edit').replace(/\/$/, '') + '/pagetualRules.json' : 'https://github.com/hoothin/UserScripts/edit/master/Pagetual/pagetualRules.json')); @@ -10427,7 +10532,7 @@ return true; } - function updateRules (success, fail, keepCache) { + function updateRules (success, fail, keepCache, forceWedataMirrorFallback) { if (!storage.supportCrossSave()) { fail({url:''}, "Not support cross storage"); showTips("Current platform do not support cross storage!"); @@ -10443,6 +10548,27 @@ let allOk = true; let preLength = ruleParser.rules.length; let fetchVersion = -1; + let triedWedataMirrorFallback = false; + function switchToWedataMirrorOnFail(rule) { + if (!rule || rule.id !== 1 || rulesData.wedata2github || (updateDate && !forceWedataMirrorFallback)) { + return false; + } + rulesData.wedata2github = true; + storage.setItem("rulesData", rulesData); + if (wedata2githubInputRef) { + wedata2githubInputRef.checked = true; + } + if (ruleUrls && ruleUrls.length > 0) { + for (let i = 0; i < ruleUrls.length; i++) { + if (ruleUrls[i].id === 1) { + ruleUrls[i].url = wedataMirrorRulesUrl; + break; + } + } + } + rule.url = wedataMirrorRulesUrl; + return true; + } async function needUpdate(url, id) { if (!/^https:\/\/hoothin\.github\.io\/|\/hoothin\/UserScripts\/.*\/Pagetual\/pagetualRules\.json/i.test(url)) { return true; @@ -10509,6 +10635,17 @@ if (await needUpdate(rule.url, rule.id)) { ruleParser.addRuleByUrl(rule.url, rule.id, (json, err) => { if (!json) { + if (!triedWedataMirrorFallback && switchToWedataMirrorOnFail(rule)) { + triedWedataMirrorFallback = true; + ruleParser.addRuleByUrl(rule.url, rule.id, (retryJson, retryErr) => { + if (!retryJson) { + allOk = false; + fail(rule, retryErr); + } + addNextRule(); + }); + return; + } allOk = false; fail(rule, err); } @@ -10626,7 +10763,7 @@ /*0 wedata格式,1 pagetual格式*/ ruleUrls = [{ id: 1, - url: data && data.wedata2github ? 'https://hoothin.github.io/UserScripts/Pagetual/items_all.json' : 'http://wedata.net/databases/AutoPagerize/items_all.json', + url: data && data.wedata2github ? wedataMirrorRulesUrl : wedataRulesUrl, type: 0 }]; if (data) { diff --git a/Pagetual/pagetualRules.json b/Pagetual/pagetualRules.json index f72854a3737..2b81ff29282 100644 --- a/Pagetual/pagetualRules.json +++ b/Pagetual/pagetualRules.json @@ -6487,5 +6487,16 @@ "replaceElement": "div.product-card", "pageElement": "div.products-catalog__list", "action": 2 +}, +{ + "name": "xHamster", + "url": "^https://([\\w]+\\.)?(xhamster|xhaccess|xhamster19)\\.(com|desi)/", + "author": "vieller", + "pageElement": "div:has(+ *[data-role*='pagination']), div:has(+ *[class*='pager-section']), div:has(+ *[class*='pagination-'])", + "nextLink": "a[data-page='next'], a[rel='next']", + "action": 1, + "init": "let candidates=Array.from(document.querySelectorAll('.thumb-list__item'));let bestCandidate=candidates.find(c=>c.querySelector('[data-role=\"video-duration\"]'));if(!bestCandidate&&candidates.length) bestCandidate=candidates[0];if(!window._pagetualMasterTemplateVideo){if(bestCandidate){let clone=bestCandidate.cloneNode(true);let badgeElements=clone.querySelectorAll('[data-role=\"video-watched\"], [data-brand=\"full video\"]');badgeElements.forEach(badge=>badge.remove());let xhIcons=clone.querySelectorAll('.xh-icon');xhIcons.forEach(icon=>icon.remove());window._pagetualMasterTemplateVideo=clone.innerHTML;let durationSpan=clone.querySelector('[data-role=\"video-duration\"] [class*=\"tiny-\"]');if(durationSpan){let tinyClass=Array.from(durationSpan.classList).find(c=>c.startsWith('tiny-'));if(tinyClass){let suffix=tinyClass.split('-').pop();if(suffix) window._pagetualSuffix=suffix;}}}} if(!window._pagetualMasterGallery){let galleryCandidates=Array.from(document.querySelectorAll('[class*=\"container-\"] > [class*=\"wrapper\"]'));if(!galleryCandidates.length) galleryCandidates=Array.from(document.querySelectorAll('div[class*=\"wrapper\"]')).filter(el=>el.querySelector('[class*=\"author-\"]'));let bestGallery=galleryCandidates.find(g=>g.querySelector('[class*=\"author-\"]'));if(!bestGallery&&galleryCandidates.length) bestGallery=galleryCandidates[0];if(bestGallery){let clone=bestGallery.cloneNode(true);window._pagetualMasterGallery=clone.innerHTML;let captionSpan=clone.querySelector('[class*=\"caption-\"]');if(captionSpan){let captionClass=Array.from(captionSpan.classList).find(c=>c.startsWith('caption-'));if(captionClass) window._pagetualCaptionSuffix=captionClass.split('-').pop();} let bodySpan=clone.querySelector('[class*=\"body-bold-\"]');if(bodySpan){let bodyClass=Array.from(bodySpan.classList).find(c=>c.startsWith('body-bold-'));if(bodyClass) window._pagetualBodySuffix=bodyClass.split('-').pop();}}} if(!window._pagetualSuffix) window._pagetualSuffix='8643e';if(!window._pagetualCaptionSuffix) window._pagetualCaptionSuffix='8643e';if(!window._pagetualBodySuffix) window._pagetualBodySuffix='8643e';if(!window._pagetualFriendsOnlyTemplate){let rootClass='root-'+window._pagetualSuffix;let iconClass='icon-'+window._pagetualSuffix;window._pagetualFriendsOnlyTemplate=`