GOG.com - Promo Filter

Hide/remove games matching your keywords on GOG.com and collapse the grid slots they occupied

  1. // ==UserScript==
  2. // @name GOG.com - Promo Filter
  3. // @namespace GOG.com - Promo Filter
  4. // @version 2.4
  5. // @description Hide/remove games matching your keywords on GOG.com and collapse the grid slots they occupied
  6. // @author masterofobzene
  7. // @homepage https://github.com/masterofobzene/UserScriptRepo
  8. // @icon https://www.gog.com/favicon.ico
  9. // @match https://www.gog.com/*/games*
  10. // @grant none
  11. // @run-at document-end
  12. // @license GNU GPLv3
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. 'use strict';
  17.  
  18. // — your forbidden words (case‐insensitive) —
  19. const filterWords = ['bundle', 'edition', 'supporter', 'halloween', 'christmas', 'soundtrack'];
  20. const hideMethod = 'remove'; // 'remove' or 'hide'
  21. const checkInterval = 1000; // ms
  22.  
  23. // core filter routine
  24. function filterGames() {
  25. document
  26. .querySelectorAll('.product-tile:not([data-filtered])')
  27. .forEach(tile => {
  28. const titleEl = tile.querySelector('.product-tile__title, .product-tile__info__name');
  29. if (!titleEl) return;
  30. const title = titleEl.textContent.trim().toLowerCase();
  31. if (filterWords.some(w => title.includes(w))) {
  32. tile.setAttribute('data-filtered', 'true');
  33.  
  34. // climb up until our parent is the real grid/flex container
  35. let wrapper = tile;
  36. while (
  37. wrapper.parentElement &&
  38. !['grid', 'flex'].includes(
  39. window.getComputedStyle(wrapper.parentElement).display
  40. )
  41. ) {
  42. wrapper = wrapper.parentElement;
  43. }
  44.  
  45. // if we found that container, remove/hide the grid‐item itself
  46. if (
  47. wrapper.parentElement &&
  48. ['grid', 'flex'].includes(
  49. window.getComputedStyle(wrapper.parentElement).display
  50. )
  51. ) {
  52. if (hideMethod === 'remove') {
  53. wrapper.remove();
  54. } else {
  55. wrapper.style.display = 'none';
  56. }
  57. } else {
  58. // fallback: hide the tile itself
  59. tile.style.display = 'none';
  60. }
  61. }
  62. });
  63. }
  64.  
  65. // set up observers & intervals
  66. function init() {
  67. filterGames();
  68.  
  69. // watch for new tiles (infinite scroll, dynamic re-render)
  70. const obs = new MutationObserver(muts => {
  71. if (muts.some(m => m.addedNodes.length > 0)) {
  72. filterGames();
  73. }
  74. });
  75. obs.observe(document.body, { childList: true, subtree: true });
  76.  
  77. // belt‐and‐suspenders: interval re-check
  78. setInterval(filterGames, checkInterval);
  79.  
  80. // clean up on page unload
  81. window.addEventListener('beforeunload', () => obs.disconnect());
  82. }
  83.  
  84. // **bootstrapping**: wait until at least one .product-tile exists
  85. const bootstrap = setInterval(() => {
  86. if (document.querySelector('.product-tile')) {
  87. clearInterval(bootstrap);
  88. init();
  89. }
  90. }, 500);
  91.  
  92. })();

QingJ © 2025

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