fix-image-error at inoreader

Fix image load error caused by CSP(Content Security Policy)

目前為 2023-12-07 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name fix-image-error at inoreader
  3. // @name:zh-CN 修复inoreader图片异常
  4. // @version 0.3.0
  5. // @namespace https://github.com/mengtao-code
  6. // @description Fix image load error caused by CSP(Content Security Policy)
  7. // @description:zh-CN 修复inoreader的图片加载问题
  8. // @author Mengtao Xin
  9. // @license MIT
  10. // @supportURL https://github.com/mengtao-code/tampermonkey-scripts
  11. // @include http*://*.inoreader.com/*
  12. // @icon http://www.inoreader.com/favicon.ico
  13. // @grant GM_xmlhttpRequest
  14. // @connect *
  15. // ==/UserScript==
  16.  
  17. const LOADING_PROMPT = "Loading..."
  18.  
  19. const config = {
  20. name: "fix-image-error",
  21. data: [ // weibo images prefix
  22. {
  23. imageServer: 'sinaimg.cn',
  24. mockHeader: {
  25. Referer: 'https://weibo.com'
  26. }
  27. }
  28. ]
  29. }
  30.  
  31. /**
  32. *
  33. * @link https://stackoverflow.com/questions/8778863/downloading-an-image-using-xmlhttprequest-in-a-userscript
  34. * @param {*} input
  35. * @returns {string}
  36. */
  37. function toBase64(input) {
  38. var
  39. bbLen = 3,
  40. enCharLen = 4,
  41. inpLen = input.length,
  42. inx = 0,
  43. jnx,
  44. keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  45. + "0123456789+/=",
  46. output = "",
  47. paddingBytes = 0;
  48. var
  49. bytebuffer = new Array(bbLen),
  50. encodedCharIndexes = new Array(enCharLen);
  51.  
  52. while (inx < inpLen) {
  53. for (jnx = 0; jnx < bbLen; ++jnx) {
  54. if (inx < inpLen)
  55. bytebuffer[jnx] = input.charCodeAt(inx++) & 0xff;
  56. else
  57. bytebuffer[jnx] = 0;
  58. }
  59. encodedCharIndexes[0] = bytebuffer[0] >> 2;
  60. encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4);
  61. encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6);
  62. encodedCharIndexes[3] = bytebuffer[2] & 0x3f;
  63. paddingBytes = inx - (inpLen - 1);
  64. switch (paddingBytes) {
  65. case 1:
  66. // Set last character to padding char
  67. encodedCharIndexes[3] = 64;
  68. break;
  69. case 2:
  70. // Set last 2 characters to padding char
  71. encodedCharIndexes[3] = 64;
  72. encodedCharIndexes[2] = 64;
  73. break;
  74. default:
  75. break; // No padding - proceed
  76. }
  77. for (jnx = 0; jnx < enCharLen; ++jnx)
  78. output += keyStr.charAt(encodedCharIndexes[jnx]);
  79. }
  80. return output;
  81. }
  82.  
  83. /**
  84. *
  85. * @param responseData data from http request
  86. * @returns {string}
  87. */
  88. const getImageUrl = (responseData) => {
  89. const binResp = toBase64(responseData.responseText);
  90. return `data:image/jpeg;base64,${binResp}`;
  91. }
  92.  
  93. /**
  94. * send get http request
  95. * @param url
  96. * @param customHeader
  97. * @returns {Promise<unknown>}
  98. */
  99. const httpGetRequest = (url, customHeader) => {
  100. return new Promise((resolve, reject) => {
  101. GM.xmlHttpRequest({
  102. method: "GET",
  103. url: url,
  104. headers: {
  105. "Accept": "*/*",
  106. "referrerPolicy": "no-referrer",
  107. ...customHeader
  108. },
  109. onload: resolve,
  110. onerror: reject,
  111. overrideMimeType: 'text/plain; charset=x-user-defined'
  112. });
  113. })
  114. }
  115.  
  116. const processDetailImage = (dom, mockHeader) => {
  117. if (dom.getAttribute('process-tag') !== 'doing' && dom.getAttribute('process-tag') !== 'done') {
  118. dom.setAttribute("process-tag", "doing")
  119. dom.setAttribute('alt', LOADING_PROMPT)
  120. const originUrl = dom.getAttribute("src");
  121. httpGetRequest(originUrl, mockHeader)
  122. .then(responseData => {
  123. const goodUrl = getImageUrl(responseData);
  124. dom.setAttribute('src', goodUrl)
  125. })
  126. .catch(e => console.error(`${config.name} load image failed! ${e}`))
  127. .finally(() => dom.setAttribute('process-tag', 'done'))
  128. }
  129. }
  130.  
  131.  
  132. const processListImage = (dom, mockHeader) => {
  133. if (dom.getAttribute('process-tag') !== 'doing' && dom.getAttribute('process-tag') !== 'done') {
  134. dom.setAttribute("process-tag", "doing")
  135. const style = dom.getAttribute("style")
  136. const originUrl = style.substring(style.indexOf("https")).replace("')", "")
  137. console.log(`originUrl:${originUrl}`)
  138. httpGetRequest(originUrl, mockHeader)
  139. .then(responseData => {
  140. const goodUrl = getImageUrl(responseData);
  141. dom.setAttribute('style', `background-image:url('${goodUrl}')`)
  142.  
  143. })
  144. .catch(e => console.error(`${config.name} load image failed! ${e}`))
  145. .finally(() => dom.setAttribute('process-tag', 'done'))
  146. }
  147. }
  148.  
  149. /**
  150. * 检测到有异常图片,就调整成正常的图片
  151. */
  152. const main = () => {
  153. config.data.forEach(({imageServer, mockHeader}) => {
  154. // 1. detail pages
  155. Array.from(
  156. document.querySelectorAll(`.article_content img[src*='${imageServer}']`))
  157. .forEach(image => processDetailImage(image, mockHeader));
  158.  
  159. // 2. list page
  160. Array.from(
  161. document.querySelectorAll(`.article_magazine_picture`))
  162. .filter(dom=>dom.getAttribute("style").includes(imageServer))
  163. .forEach(image => processListImage(image, mockHeader));
  164. })
  165. }
  166.  
  167. setInterval(main, 3000)

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址