SmartFrame Downloader

Enables the download of high-resolution PNG images from a 'SmartFrame'

  1. // ==UserScript==
  2. // @name SmartFrame Downloader
  3. // @namespace hoixw
  4. // @version 1.1
  5. // @description Enables the download of high-resolution PNG images from a 'SmartFrame'
  6. // @author hoixw
  7. // @match *://*/*
  8. // @grant none
  9. // @run-at document-start
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. // Check if the flag is set in localStorage (uses run-at document start)
  14. if (localStorage.getItem('runCodeAfterRefresh') === 'true') {
  15. // Remove the flag from localStorage
  16. localStorage.removeItem('runCodeAfterRefresh');
  17.  
  18. /*
  19. This forces all shadow-roots created to be open. Closed shadow-roots, as used by the
  20. smartFrame, cannot be accessed through JS. That's why they exist.
  21. Open shadow-roots can, so this means we can just access it and grab the image.
  22. */
  23. const nativeAttachShadow = Element.prototype.attachShadow;
  24. // Override attachShadow but force mode open
  25. Element.prototype.attachShadow = function(init) {
  26. init.mode = "open";
  27. return nativeAttachShadow.call(this, init);
  28. };
  29.  
  30. // Mask the override by patching toString so it returns a benign string
  31. Object.defineProperty(Element.prototype.attachShadow, 'toString', {
  32. value: function() {
  33. return "function attachShadow() { [native code] }";
  34. },
  35. writable: false,
  36. });
  37.  
  38. var checkForSmartFrame = setInterval(function() {
  39. if (document.querySelector('smartframe-embed')) {
  40. clearInterval(checkForSmartFrame); // Stop checking once the element is found
  41.  
  42. var smartFrame = document.querySelector('smartframe-embed');
  43. var styles = getComputedStyle(smartFrame);
  44. // Actual width and height of image are stored here
  45. var width = smartFrame.style.getPropertyValue('--sf-original-width');
  46. var height = smartFrame.style.getPropertyValue('--sf-original-height');
  47.  
  48. // Correct width and height variables
  49. smartFrame.style.width = width + "px";
  50. smartFrame.style.maxWidth = width + "px";
  51. smartFrame.style.height = height + "px";
  52. smartFrame.style.maxHeight = height + "px";
  53.  
  54. setTimeout(() => {
  55. var stage = smartFrame.shadowRoot.querySelector("canvas.stage");
  56. var url = document.createElement("canvas").toDataURL.call(stage);
  57. var a = document.createElement("a");
  58. var t = smartFrame.getAttribute("image-id").replace(/\s/g, '-') + ".png";
  59. a.href = url;
  60. a.download = t;
  61. a.click();
  62. }, 3000);
  63. }
  64. }, 1000);
  65. } else {
  66. // Wait for the page to load completely (otherwise smart-frame won't exist)
  67. window.addEventListener('load', function() {
  68. if (document.querySelector('smartframe-embed')) {
  69. 'use strict';
  70.  
  71. var executeButton = document.createElement('button');
  72. executeButton.textContent = 'Download Hi-Res';
  73.  
  74. executeButton.style.padding = '10px 20px';
  75. executeButton.style.backgroundColor = '#007BFF';
  76. executeButton.style.color = 'white';
  77. executeButton.style.border = 'none';
  78. executeButton.style.borderRadius = '5px';
  79. executeButton.style.cursor = 'pointer';
  80. executeButton.style.fontWeight = 'bold';
  81. executeButton.style.margin = '10px';
  82.  
  83. // Attach a click event listener to the button
  84. executeButton.addEventListener('click', function() {
  85. // Sets localStorage flag for code to be run after refresh immediately
  86. localStorage.setItem('runCodeAfterRefresh', 'true');
  87.  
  88. location.reload();
  89. });
  90.  
  91. var smartFrameParent = document.querySelector('smartframe-embed').parentElement;
  92. smartFrameParent.appendChild(executeButton);
  93. }
  94. });
  95. }

QingJ © 2025

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