Old Reddit Top Filters (1h,6h,12h,24h,W,2W,M,Y,all)

Add quick quick filter buttons to tab menu. Adds 6h,12h,2w buttons (fetch+filter 1 page per second).

// ==UserScript==
// @name         Old Reddit Top Filters (1h,6h,12h,24h,W,2W,M,Y,all)
// @description  Add quick quick filter buttons to tab menu. Adds 6h,12h,2w buttons (fetch+filter 1 page per second).
// @version      2.2
// @author      C89sd
// @namespace   https://gf.qytechs.cn/users/1376767
// @match       https://old.reddit.com/*
// @noframes
// ==/UserScript==

const MS_TABLE = { h: 3600e3, d: 86400e3, w: 604800e3, m: 2592e6, y: 31536e6 };
const BUTTONS = [
    { lbl: '1h',  t: 'hour',  filter: ''   },
    //{ lbl: '3h',  t: 'day',   filter: '3h' }, // may scrape too many pages on large subs
    //{ lbl: '4h',  t: 'day',   filter: '4h' },
    { lbl: '6h',  t: 'day',   filter: '6h' },
    { lbl: '12h', t: 'day',   filter: '12h'},
    { lbl: '24h', t: 'day',   filter: ''   },
    { lbl: 'W',   t: 'week',  filter: ''   },
    { lbl: '2W',  t: 'month', filter: '2w' },
    { lbl: 'M',   t: 'month', filter: ''   },
    //{ lbl: '3M',   t: 'year',  filter: '3m'   }, // may scrape too many pages on large subs
    //{ lbl: '6M',   t: 'year',  filter: '6m'   },
    { lbl: 'Y',   t: 'year',  filter: ''   },
    { lbl: 'all', t: 'all',   filter: ''   },
];
const FILTER_REGEX = /#filter=(\d+)([hdwmy])/i;
const MIN_POSTS = 25;
const FETCH_DELAY_MS = 750;

window.filterClick = function filterClick(e){
    const hrefClean = e.currentTarget.href.replace(/#.*/,'');
    const hereClean = location.href.replace(/#.*/,'');
    if (hrefClean === hereClean) setTimeout(() => location.reload(), 100);
};

const tabMenu = document.querySelector('.tabmenu');
if (tabMenu) {
    let fetchController = new AbortController();
    window.addEventListener('pagehide', () => fetchController.abort());

    const { origin, pathname } = window.location;
    const isFrontOrAllPage = pathname === '/' || pathname.startsWith('/r/all');
    const pathPrefix = (pathname.match(/^(\/r\/[^/]+|\/user\/[^/]+\/m\/[^/]+)/) || [''])[0];
    const baseUrl = `${origin}${pathPrefix}/top/?sort=top`;

    const styleTag = document.createElement('style');
    styleTag.textContent = '.ggray { filter: saturate(0.1) !important; }';
    document.head.appendChild(styleTag);

    const tMatch      = location.search.match(/[?&]t=([^&#]+)/i);
    const curT        = tMatch ? tMatch[1] : '';
    const curFilterRx = location.hash.match(FILTER_REGEX);
    const curFilter   = curFilterRx ? curFilterRx[1] + curFilterRx[2] : '';

    const buttonsHTML = BUTTONS
      .filter(btn => !(isFrontOrAllPage && btn.t === 'year'))
      .map(btn => {
          const url   = `${baseUrl}&t=${btn.t}`;
          const href  = btn.filter ? `${url}#filter=${btn.filter}` : url;
          const click = btn.filter ? 'onclick="filterClick(event)"' : '';

          const isSel = curT === btn.t && curFilter === btn.filter;
          const liCls = isSel ? 'selected' : (btn.filter ? 'ggray' : '');

          return `<li class="${liCls}"><a ${click} href="${href}">${btn.lbl}</a></li>`;
      })
      .join('');
    tabMenu.insertAdjacentHTML('beforeend', buttonsHTML);

    const runCustomFilter = () => {
        const hashMatch = window.location.hash.match(FILTER_REGEX);
        if (!hashMatch) return;

        const siteTable = document.getElementById('siteTable');
        if (!siteTable) return;

        fetchController.abort();
        fetchController = new AbortController();
        const { signal } = fetchController;

        const [, amount, unit] = hashMatch;
        const cutoffTimestamp = Date.now() - parseInt(amount, 10) * MS_TABLE[unit.toLowerCase()];

        const loader = document.createElement('div');
        loader.style.cssText = 'padding: 15px; font-weight: bold; display: none;';
        siteTable.insertAdjacentElement('afterend', loader);

        const navButtons = document.querySelector('#siteTable .nav-buttons');
        if (navButtons) siteTable.insertAdjacentElement('afterend', navButtons);

        const isTooOld = p => (p.dataset.timestamp || 0) < cutoffTimestamp;
        const filterPosts = () =>
            siteTable.querySelectorAll('.thing[data-timestamp]').forEach(p => isTooOld(p) && p.remove());

        const updateNext = href => {
            const nextLink = document.querySelector('.next-button a');
            if (nextLink) {
                if (href) nextLink.href = href.replace(/#.*/, '') + window.location.hash;
                else nextLink.parentElement.remove();
            }
        };

        const fetchPage = async url => {
            const startTime = Date.now();
            const html = await (await fetch(url, { credentials: 'include', signal })).text();
            const doc  = new DOMParser().parseFromString(html, 'text/html');
            const endTime = Date.now();
            const duration = endTime - startTime;

            const remainingDelay = Math.max(0, FETCH_DELAY_MS - duration);
            if (remainingDelay > 0) {
              await new Promise(r => setTimeout(r, remainingDelay));
            }

            return {
                posts: doc.querySelectorAll('#siteTable .thing'),
                next : doc.querySelector('.next-button a')?.href || ''
            };
        };

        (async () => {
            filterPosts();
            let nextUrl = document.querySelector('.next-button a')?.href;
            let fetchCount = 0;

            while (document.querySelectorAll('#siteTable .thing').length < MIN_POSTS && nextUrl) {
                if (signal.aborted) break;
                loader.textContent = `⏳ Filtering and loading more posts... ${++fetchCount}`;
                loader.style.display = 'block';

                try {
                    const { posts, next } = await fetchPage(nextUrl);
                    posts.forEach(p => siteTable.appendChild(p));
                    filterPosts();
                    nextUrl = next;
                    updateNext(nextUrl);
                } catch (err) {
                    if (err.name !== 'AbortError') {
                        loader.textContent = 'An error occurred while loading posts.';
                        console.error('[Reddit Filter Userscript] Fetch Error:', err);
                    }
                    break;
                }
            }
            loader.style.display = 'none';
        })();
    };
    window.runCustomFilter = runCustomFilter;

    window.addEventListener('pageshow', e => { if (!e.persisted) runCustomFilter(); });
}

QingJ © 2025

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