Twitterᴾˡᵘˢ

Enhance Twitter user experience. Load images in original quality, remove tweets that contain specific hashtags or exceed the maximum limit.

Per 22-11-2023. Zie de nieuwste versie.

  1. // ==UserScript==
  2. // @name Twitterᴾˡᵘˢ
  3. // @name:zh-TW Twitterᴾˡᵘˢ
  4. // @name:zh-CN Twitterᴾˡᵘˢ
  5. // @name:ja Twitterᴾˡᵘˢ
  6. // @namespace https://greasyfork.org
  7. // @version 0.3.0
  8. // @description Enhance Twitter user experience. Load images in original quality, remove tweets that contain specific hashtags or exceed the maximum limit.
  9. // @description:zh-TW 增強Twitter使用者體驗。讀取原始畫質的圖片,移除包含特定Hashtag或超過最大限制的推文。
  10. // @description:zh-CN 增强Twitter使用者体验。读取原始画质的图片,移除包含特定Hashtag或超过最大限制的推文。
  11. // @description:ja Twitterのユーザー体験を向上させます。元の品質で画像をロードし、特定のハッシュタグを含むまたは最大限度を超えるツイートを非表示にします。
  12. // @author Pixmi
  13. // @icon https://www.google.com/s2/favicons?sz=64&domain=twitter.com
  14. // @match https://twitter.com/*
  15. // @match https://mobile.twitter.com/*
  16. // @match https://pbs.twimg.com/media/*
  17. // @license AGPL-3.0-or-later
  18. // @grant GM_setValue
  19. // @grant GM_getValue
  20. // @grant GM_addStyle
  21. // ==/UserScript==
  22. // Hide the post if the hashtag exceeds the set number. (If set to 0, it will not be enabled)
  23. if (GM_getValue('MAX_HASHTAGS') == undefined) { GM_setValue('MAX_HASHTAGS', 20); }
  24. // Hide the post if it contains the following hashtag. (Please include "#" and separate using commas)
  25. if (GM_getValue('OUT_HASHTAGS') == undefined) { GM_setValue('OUT_HASHTAGS', '#tag1,#tag2'); }
  26. // Change OUT_HASHTAGS type to string
  27. if (typeof GM_getValue('OUT_HASHTAGS') == 'object') { GM_setValue('OUT_HASHTAGS', GM_getValue('OUT_HASHTAGS').join(',')); }
  28.  
  29. (function () {
  30. 'use strict';
  31. const URL = window.location.href;
  32. const twimg = URL.match(/https:\/\/(pbs\.twimg\.com\/media\/[a-zA-Z0-9\-\_]+)(\?format=|.)(jpg|jpeg|png|webp)/);
  33. // browsing an image URL
  34. if (twimg) {
  35. // webp change to jpg
  36. if (twimg[3] == 'webp') twimg[3] = 'jpg';
  37. // change it to obtain the original quality.
  38. if (twimg[2] == '?format=' || !/name=orig/.test(URL)) {
  39. window.location.replace(`https://${twimg[1]}.${twimg[3]}?name=orig`);
  40. }
  41. }
  42. // if browsing tweets, activate the observer.
  43. if (URL.includes('twitter.com')) {
  44. const rootmatch = document.evaluate('//div[@id="react-root"]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
  45. const rootnode = rootmatch.singleNodeValue;
  46. const MAX_HASHTAGS = GM_getValue('MAX_HASHTAGS');
  47. const OUT_HASHTAGS = GM_getValue('OUT_HASHTAGS').split(',');
  48. if (rootnode) {
  49. const callback = (mutationsList, observer) => {
  50. for (let mutation of mutationsList) {
  51. let target = mutation.target;
  52. if (target.nodeName !== 'ARTICLE' || !target.classList.contains('css-175oi2r')) continue;
  53. let hashtags = Array.from(target.querySelectorAll('.css-1rynq56 > .r-18u37iz'), tag => tag.textContent);
  54. let hideCheck = false;
  55. if (hashtags.length) {
  56. if (MAX_HASHTAGS > 0 &&hashtags.length >= MAX_HASHTAGS) hideCheck = true;
  57. if (hashtags.some(tag => OUT_HASHTAGS.find(item => item == tag))) hideCheck = true;
  58. if (hideCheck) {
  59. target.closest('div[data-testid="cellInnerDiv"] > div').style.display = 'none';
  60. target.remove();
  61. continue;
  62. }
  63. }
  64. let images = target.getElementsByTagName('img');
  65. if (!images.length) continue;
  66. for (const image of images) {
  67. let match = image.src.match(/https:\/\/(pbs\.twimg\.com\/media\/[a-zA-Z0-9\-\_]+)(\?format=|.)(jpg|jpeg|png|webp)/);
  68. if (!match) continue;
  69. // Image url change to original quality.
  70. if (match[3] == 'webp') match[3] = 'jpg';
  71. if (match[2] == '?format=' || !/name=orig/.test(image.src)) {
  72. image.src = `https://${match[1]}.${match[3]}?name=orig`;
  73. }
  74. }
  75. }
  76. };
  77. const observeConfig = {
  78. attributes: true,
  79. childList: true,
  80. subtree: true
  81. };
  82. const observer = new MutationObserver(callback);
  83.  
  84. observer.observe(document.body, observeConfig);
  85. }
  86. }
  87. })();