Extract images for Twitter

Adds a button that opens all attached images as original size to every tweet.

  1. // ==UserScript==
  2. // @name Extract images for Twitter
  3. // @name:ja Extract images for Twitter
  4. // @namespace https://gf.qytechs.cn/ja/users/24052-granony
  5. // @description Adds a button that opens all attached images as original size to every tweet.
  6. // @description:ja 各ツィートに,添付されている画像をオリジナルのサイズで開くためのボタンを追加します.
  7. // @include https://twitter.com/*
  8. // @author granony
  9. // @version 2.0.1
  10. // @grant none
  11. // @license MIT License
  12. // ==/UserScript==
  13. (function () {
  14. var processedLists = new WeakMap();
  15. // ボタンの作成
  16. var createButton = function (list) {
  17. var images = list.parentNode.parentNode.getElementsByClassName('AdaptiveMedia-photoContainer');
  18. var button = document.createElement('div');
  19. button.setAttribute('class', 'ProfileTweet-action js-toggleState ProfileTweet-action--ExtractImages');
  20. button.innerHTML = '<button class="ProfileTweet-actionButton js-actionButton" type="button">'+
  21. '<div class="IconContainer js-tooltip" title="Extract Images">'+
  22. '<span class="Icon Icon--photo"></span>'+
  23. '</div>'+
  24. '</button>';
  25. button.addEventListener('click', function () {
  26. for (var j = 0; j < images.length; j++) {
  27. var url = images[j].getAttribute('data-image-url') + ':orig';
  28. window.open(url);
  29. }
  30. });
  31. button.addEventListener('mouseenter',function(){
  32. if(images.length<1){
  33. return;
  34. }
  35. var icon = button.getElementsByClassName("Icon")[0];
  36. icon.style.color = "rgb(47,194,239)";
  37. })
  38. button.addEventListener('mouseleave',function(){
  39. var icon = button.getElementsByClassName("Icon")[0];
  40. icon.style.color = "rgb(170,184,194)";
  41. })
  42. return button;
  43. };
  44. // 新規に出現したtweetsに対してボタンを追加
  45. var addButtons = function () {
  46. var lists = document.getElementsByClassName('ProfileTweet-actionList');
  47. for (var i = 0; i < lists.length; i++) {
  48. var list = lists[i];
  49. if (processedLists.has(list)) {
  50. continue;
  51. } else {
  52. // なぜか画面遷移前のボタンが残ることがあるので,暫定的に対処
  53. var oldButton = list.getElementsByClassName("ProfileTweet-action--ExtractImages")[0];
  54. if(oldButton){
  55. oldButton.parentNode.removeChild(oldButton);
  56. }
  57. // 実際に登録
  58. processedLists.set(list, 1);
  59. var button = createButton(list);
  60. list.appendChild(button);
  61. }
  62. }
  63. };
  64. // ツィートの表示件数が増えた場合
  65. (function () {
  66. var DOMObserverTimer = false;
  67. var DOMObserverConfig = {
  68. attributes: true,
  69. childList: true,
  70. subtree: true
  71. };
  72. var DOMObserver = new MutationObserver(function () {
  73. if (DOMObserverTimer !== 'false') {
  74. clearTimeout(DOMObserverTimer);
  75. }
  76. DOMObserverTimer = setTimeout(function () {
  77. DOMObserver.disconnect();
  78. addButtons();
  79. DOMObserver.observe(document.body, DOMObserverConfig);
  80. }, 100);
  81. });
  82. DOMObserver.observe(document.body, DOMObserverConfig);
  83. }) ();
  84. // 初回実行
  85. addButtons();
  86. }) ();

QingJ © 2025

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