Sleazy Fork is available in English.

Kemono.cr Attachment Link Manager

Fetch and manage attachment links on Kemono.cr posts with filter support

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Kemono.cr Attachment Link Manager
// @namespace    http://tampermonkey.net/
// @version      1.0.2
// @description  Fetch and manage attachment links on Kemono.cr posts with filter support
// @author       viatana35
// @license      GNUGPLV3
// @match        https://kemono.cr/patreon/user/*/post/*
// @grant        GM_download
// @grant        GM_notification
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// ==/UserScript==

(function() {
    'use strict';

    // Storage keys
    const STORAGE_KEY = 'kemono_attachment_links';
    const BANNED_WORDS_KEY = 'kemono_banned_words';

    // Function to fetch all attachment links on the page, filtered by banned words
    function fetchAttachmentLinks() {
        const links = [];
        const bannedWords = JSON.parse(localStorage.getItem(BANNED_WORDS_KEY) || '[]');
        const attachmentElements = document.querySelectorAll('ul.post__attachments li.post__attachment a.post__attachment-link');

        if (attachmentElements.length === 0) {
            alert('No attachment links found on this page.');
            return links;
        }

        attachmentElements.forEach(element => {
            const href = element.getAttribute('href');
            const text = element.textContent.toLowerCase();
            if (href) {
                let isBanned = false;
                bannedWords.forEach(word => {
                    if (text.includes(word.toLowerCase())) {
                        isBanned = true;
                    }
                });
                if (!isBanned) {
                    links.push(href);
                }
            }
        });
        return links;
    }

    // Function to save links to localStorage
    function saveLinksToStorage(links) {
        const existingLinks = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
        const newLinks = links.filter(link => !existingLinks.includes(link));
        if (newLinks.length > 0) {
            const updatedLinks = [...existingLinks, ...newLinks];
            localStorage.setItem(STORAGE_KEY, JSON.stringify(updatedLinks));
        }
    }

    // Function to get the number of stored links
    function getStoredLinksCount() {
        const links = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
        return links.length;
    }

    // Function to download links as a text file
    function downloadLinksAsText() {
        const links = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
        if (links.length === 0) {
            alert('No links to download.');
            return;
        }
        const blob = new Blob([links.join('\n')], { type: 'text/plain' });
        const url = URL.createObjectURL(blob);
        GM_download({
            url: url,
            name: 'kemono_attachment_links.txt',
            saveAs: true
        });
    }

    // Function to remove current page links from localStorage
    function removeCurrentPageLinks() {
        const currentLinks = fetchAttachmentLinks();
        const storedLinks = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
        const updatedLinks = storedLinks.filter(link => !currentLinks.includes(link));
        localStorage.setItem(STORAGE_KEY, JSON.stringify(updatedLinks));
        alert('Current page links removed from storage.');
    }

    // Function to remove all links from localStorage
    function removeAllLinks() {
        if (confirm('Are you sure you want to delete all links?')) {
            localStorage.removeItem(STORAGE_KEY);
            alert('All links removed from storage.');
        }
    }

    // Function to add a banned word
    function addBannedWord() {
        const wordInput = document.getElementById('bannedWordInput');
        const word = wordInput.value.trim();
        if (word === '') {
            alert('Please enter a word to ban.');
            return;
        }
        const bannedWords = JSON.parse(localStorage.getItem(BANNED_WORDS_KEY) || '[]');
        if (!bannedWords.includes(word)) {
            bannedWords.push(word);
            localStorage.setItem(BANNED_WORDS_KEY, JSON.stringify(bannedWords));
            wordInput.value = '';
            updateBannedWordsDropdown();
        }
    }

    // Function to remove a banned word
    function removeBannedWord(word) {
        const bannedWords = JSON.parse(localStorage.getItem(BANNED_WORDS_KEY) || '[]');
        const updatedWords = bannedWords.filter(w => w !== word);
        localStorage.setItem(BANNED_WORDS_KEY, JSON.stringify(updatedWords));
        updateBannedWordsDropdown();
    }

    // Function to clear all banned words
    function clearAllBannedWords() {
        if (confirm('Are you sure you want to clear all banned words?')) {
            localStorage.removeItem(BANNED_WORDS_KEY);
            updateBannedWordsDropdown();
        }
    }

    // Function to update the banned words dropdown
    function updateBannedWordsDropdown() {
        const dropdown = document.getElementById('bannedWordsDropdown');
        dropdown.innerHTML = '';
        const bannedWords = JSON.parse(localStorage.getItem(BANNED_WORDS_KEY) || '[]');

        if (bannedWords.length === 0) {
            dropdown.innerHTML = '<div style="padding: 5px;">No banned words.</div>';
            return;
        }

        const clearAllButton = document.createElement('button');
        clearAllButton.textContent = 'Clear All';
        clearAllButton.style.marginBottom = '5px';
        clearAllButton.addEventListener('click', clearAllBannedWords);
        dropdown.appendChild(clearAllButton);

        bannedWords.forEach(word => {
            const wordDiv = document.createElement('div');
            wordDiv.style.display = 'flex';
            wordDiv.style.justifyContent = 'space-between';
            wordDiv.style.padding = '5px';

            const wordSpan = document.createElement('span');
            wordSpan.textContent = word;
            wordSpan.style.color = 'black';

            const removeButton = document.createElement('button');
            removeButton.textContent = '✕';
            removeButton.style.marginLeft = '5px';
            removeButton.addEventListener('click', () => removeBannedWord(word));

            wordDiv.appendChild(wordSpan);
            wordDiv.appendChild(removeButton);
            dropdown.appendChild(wordDiv);
        });
    }

    // Function to create the control panel
    function createControlPanel() {
        const panel = document.createElement('div');
        panel.style.position = 'fixed';
        panel.style.top = '10px';
        panel.style.right = '10px';
        panel.style.zIndex = '9999';
        panel.style.backgroundColor = '#f0f0f0';
        panel.style.padding = '10px';
        panel.style.borderRadius = '5px';
        panel.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)';

        // Download button
        const downloadButton = document.createElement('button');
        downloadButton.textContent = `Download List (${getStoredLinksCount()})`;
        downloadButton.style.marginRight = '10px';
        downloadButton.addEventListener('click', downloadLinksAsText);

        // Delete current links button
        const removeCurrentButton = document.createElement('button');
        removeCurrentButton.textContent = 'Delete Current Links';
        removeCurrentButton.style.marginRight = '10px';
        removeCurrentButton.addEventListener('click', removeCurrentPageLinks);

        // Delete all button
        const removeAllButton = document.createElement('button');
        removeAllButton.textContent = 'Delete All';
        removeAllButton.style.marginRight = '10px';
        removeAllButton.addEventListener('click', removeAllLinks);

        // Filter input and button
        const filterDiv = document.createElement('div');
        filterDiv.style.marginTop = '10px';

        const wordInput = document.createElement('input');
        wordInput.type = 'text';
        wordInput.id = 'bannedWordInput';
        wordInput.placeholder = 'Enter word to ban';
        wordInput.style.marginRight = '5px';

        const addWordButton = document.createElement('button');
        addWordButton.textContent = 'Add';
        addWordButton.addEventListener('click', addBannedWord);

        filterDiv.appendChild(wordInput);
        filterDiv.appendChild(addWordButton);

        // Show banned words button
        const showBannedWordsButton = document.createElement('button');
        showBannedWordsButton.textContent = 'Show Banned Words';
        showBannedWordsButton.style.marginTop = '5px';
        showBannedWordsButton.addEventListener('click', () => {
            const dropdown = document.getElementById('bannedWordsDropdown');
            dropdown.style.display = dropdown.style.display === 'block' ? 'none' : 'block';
        });

        // Banned words dropdown
        const bannedWordsDropdown = document.createElement('div');
        bannedWordsDropdown.id = 'bannedWordsDropdown';
        bannedWordsDropdown.style.display = 'none';
        bannedWordsDropdown.style.position = 'absolute';
        bannedWordsDropdown.style.right = '0';
        bannedWordsDropdown.style.top = '100%';
        bannedWordsDropdown.style.backgroundColor = '#fff';
        bannedWordsDropdown.style.border = '1px solid #ccc';
        bannedWordsDropdown.style.padding = '5px';
        bannedWordsDropdown.style.borderRadius = '5px';
        bannedWordsDropdown.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)';
        bannedWordsDropdown.style.zIndex = '10000';
        bannedWordsDropdown.style.width = '200px';

        panel.appendChild(downloadButton);
        panel.appendChild(removeCurrentButton);
        panel.appendChild(removeAllButton);
        panel.appendChild(filterDiv);
        panel.appendChild(showBannedWordsButton);
        panel.appendChild(bannedWordsDropdown);

        document.body.appendChild(panel);
        updateBannedWordsDropdown();
    }

    // Main function to run on page load
    function main() {
        console.log('Kemono.cr Attachment Link Manager script running...');
        const links = fetchAttachmentLinks();
        if (links.length > 0) {
            saveLinksToStorage(links);
        }
        createControlPanel();
    }

    // Wait for the page to load
    window.addEventListener('load', function() {
        const checkExist = setInterval(function() {
            if (document.querySelector('a.header-link.home')) {
                clearInterval(checkExist);
                main();
            }
        }, 100);
    });
})();