Greasy Fork

来自缓存

Greasy Fork is available in English.

VK Feed Filter - No Images/Video

Removes posts containing images/videos from VK feed using periodic checks.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         VK Feed Filter - No Images/Video
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  Removes posts containing images/videos from VK feed using periodic checks.
// @author       AL
// @match        *://vk.com/feed*
// @grant        GM_addStyle
// @run-at       document-idle
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    const SCRIPT_NAME = "VK Feed Filter v1.5 (Interval)";
    const CHECK_INTERVAL_MS = 1500; // Как часто проверять новые посты (в миллисекундах)
    const DEBUG = true; // Включить подробное логирование

    function log(...args) {
        if (DEBUG) {
            console.log(`${SCRIPT_NAME}:`, ...args);
        }
    }
    function error(...args) {
        console.error(`${SCRIPT_NAME}:`, ...args);
    }

    log("Script activated.");

    // --- Inject CSS for hiding ---
    const styleId = 'vk-feed-filter-style-v15';
    const hideCssClass = 'vk-feed-filter-hide-v15';
    const oldStyle = document.getElementById(styleId);
    if(oldStyle) oldStyle.remove();
    // Делаем скрытие максимально надежным
    GM_addStyle(`
        .${hideCssClass} {
            display: none !important;
            visibility: hidden !important;
            height: 0 !important;
            overflow: hidden !important;
            margin: 0 !important;
            padding: 0 !important;
            border: none !important;
            content-visibility: hidden !important; /* Экспериментальное свойство, может помочь */
        }
    `);
    log("Injected CSS for hiding.");

    // --- Selectors (можно оставить из v1.4, они вроде работали для поиска) ---
    const mediaSelectors = [
        // Images & Grids
        '.MediaGrid__thumb', '.MediaGrid__image', 'img.MediaGrid__image',
        '.page_post_sized_thumbs', '.page_post_thumb_wrap img', '.page_preview_photo_image',
        '.thumb_map img', '.thumb_photo', '.PhotoPrimaryAttachment__image',
        'a[aria-label*="фотография"]', 'a[aria-label*="photo"]',
        '.article_snippet--photo_preview',
        '.page_cover',

        // Videos
        '.thumb_video', '.Video', '.page_video_thumb_wrap', '.page_video_inline_wrap',
        '.VideoInlinePlayer', '.VideoThumbnail__thumb', '.page_video_wrap', 'video',
        'iframe[src*="youtube.com"]', 'iframe[src*="vk.com/video_ext.php"]',
        '.videoplayer_media', '.VideoCard__thumb',

        // Clips
        '.ShortVideoFeedItem__playerContainer', '.short_video_feed_item', '.VerticalVideoLayer',
        '.tiktok_video_inline', '.tiktok_video_player',

        // Carousels
        '.ui_gallery__item', '.wall_carousel_item--photo', '.wall_carousel_item--video',
        '.CarouselBasic__item', '.ui_gallery_inner',

        // Others
        '.media_preview', '.ArticleSnippet__imageContainer', '.LinkSnippet__image',
        '.StorySnippet', '.PodcastSnippet__cover', '.poll_graph',
        '.audio_stack_cover',

        // Nested
        '.wall_text img', '.copy_quote img',

        // Specific post types with inherent media
        '.post_type_report',
        '.post_type_map',
        '.post_type_graffiti'
    ].join(', ');

    const postContainerSelector = '.feed_row'; // Контейнер, который обычно виден
    const actualPostSelector = '._post, .article'; // Где искать контент
    const adContainerSelectors = '._ads_block_data_w, .ads_ad_box_legacy, div[data-ad-view]';
    const allContentSelectors = `${postContainerSelector}, ${adContainerSelectors}`; // Объединяем для сканирования

    let checkIntervalId = null;
    let isChecking = false; // Флаг, чтобы предотвратить одновременный запуск проверки

    // --- Function to hide an element ---
    function hideElement(element, reason = 'Media found') {
        if (!element || element.classList.contains(hideCssClass)) {
            return false;
        }
        log(`Hiding element (ID: ${element.id || 'N/A'}, Class: ${element.classList[0] || ''}): ${reason}`);
        element.classList.add(hideCssClass);
        return true;
    }

    // --- Function to scan and hide posts ---
    function scanAndHide() {
        if (isChecking) {
            // log("Scan already in progress, skipping.");
            return;
        }
        isChecking = true;
        // log("Scanning for media posts...");

        let hiddenNow = 0;
        const elementsToCheck = document.querySelectorAll(allContentSelectors);

        if (elementsToCheck.length === 0) {
             // log("No potential post/ad containers found in this scan.");
             isChecking = false;
             return;
        }

        elementsToCheck.forEach(element => {
            if (element.classList.contains(hideCssClass)) {
                return; // Already hidden by us
            }

            // Определяем, где искать медиа
            let contentElement = element;
            if (element.matches(postContainerSelector)) {
                contentElement = element.querySelector(actualPostSelector) || element.querySelector(adContainerSelectors);
                 if (!contentElement) {
                     contentElement = element; // Проверяем саму строку, если внутри ничего нет
                 }
            }

            if (!contentElement) return; // Не удалось найти элемент для проверки

            // Проверяем наличие медиа
            try {
                const containsMedia = contentElement.querySelector(mediaSelectors);
                if (containsMedia) {
                    if (hideElement(element, `Found media: ${containsMedia.tagName}.${containsMedia.classList[0] || ''}`)) {
                        hiddenNow++;
                    }
                }
            } catch (e) {
                error("Error during querySelector:", e, "on element:", contentElement);
            }
        });

        // if (hiddenNow > 0) {
        //    log(`Hid ${hiddenNow} new elements in this scan.`);
        // }
        isChecking = false;
    }

    // --- Start the periodic check ---
    function startIntervalCheck() {
        log(`Starting periodic check every ${CHECK_INTERVAL_MS}ms.`);
        // Perform an initial scan almost immediately
        setTimeout(scanAndHide, 100); // Небольшая задержка после старта
        // Then start the interval
        if (checkIntervalId) {
            clearInterval(checkIntervalId);
        }
        checkIntervalId = setInterval(scanAndHide, CHECK_INTERVAL_MS);

        // Optional: Clear interval on page unload
        window.addEventListener('unload', () => {
            if (checkIntervalId) {
                clearInterval(checkIntervalId);
                log("Stopped periodic check.");
            }
        });
         // Pause checking when tab is hidden
         document.addEventListener('visibilitychange', () => {
             if (document.visibilityState === 'hidden') {
                 if (checkIntervalId) {
                    // clearInterval(checkIntervalId);
                    // checkIntervalId = null;
                     log("Tab hidden, pausing checks (optional - currently checks continue).");
                     // Если раскомментировать clearInterval, то проверки остановятся при неактивной вкладке
                 }
             } else {
                 if (!checkIntervalId) {
                     log("Tab visible, restarting checks.");
                     checkIntervalId = setInterval(scanAndHide, CHECK_INTERVAL_MS);
                      // Run scan immediately on becoming visible
                      setTimeout(scanAndHide, 100);
                 }
             }
         });
    }

    // --- Initialization ---
    log("Waiting for DOM Content Loaded...");
    if (document.readyState === 'interactive' || document.readyState === 'complete') {
        log("DOM already ready.");
        startIntervalCheck();
    } else {
        window.addEventListener('DOMContentLoaded', () => {
            log("DOMContentLoaded event fired.");
            startIntervalCheck();
        }, { once: true });
    }
     // Fallback just in case
     window.addEventListener('load', () => {
         if (!checkIntervalId) {
             log("Window load event fired. Ensuring checks are running.");
             startIntervalCheck();
         }
     }, { once: true });

})();