修复inoreader图片异常

修复inoreader的图片加载问题

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

QingJ © 2025

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