coomer-optimizer

Improves coomer.party

// ==UserScript==
// @name         coomer-optimizer
// @namespace    https://coomer.su/
// @version      2.5
// @description  Improves coomer.party
// @match        https://coomer.su/*
// @grant        GM_addStyle
// @grant        GM_setClipboard
// @license      MIT
// @homepage     https://greasyfork.org/en/scripts/468758-coomer-optimizer
// ==/UserScript==

(function() {
    'use strict';

   
    /**
     * Extract the summary object from the URL
     */
    const extractSummary = function(url, orUser) {
        let m = url.match(/user\/([^\/]+)/);
        if (m) {
            let userKey = key(m[1]);
            let summary = JSON.parse(localStorage.getItem(userKey));
            if (summary && !summary.user) {
                summary.user = m[1];
            }
            // console.debug('URL: ', url, " --> key: ", userKey, " --> summary: ", summary);
            return summary ? summary : (orUser ? m[1] : summary);
        } else {
            // console.debug('URL: ', url, " --> no user");
            return false;
        }

    }

    const key = function(_usr) {
        return `copt_${_usr}`;
    }

    let userPageURLMatch = window.location.pathname.match(/\/user\/([^/]+)$/);
    let userPostsPageURLMatch = window.location.pathname.match(/\/user\/([^/]+)\/post/);
    let artistsPageURLMatch = window.location.pathname.match(/\/artists/);
    let postsPageURLMatch = window.location.pathname.match(/\/posts/);

    if (userPageURLMatch || userPostsPageURLMatch) {
        let pageUser = false;
        let userPageCSSPrefix = false;
        if (userPageURLMatch) {
            pageUser = userPageURLMatch[1];
            userPageCSSPrefix = 'user-header';
        } else if (userPostsPageURLMatch) {
            pageUser = userPostsPageURLMatch[1];
            userPageCSSPrefix = 'post';
        }
        console.debug(`User page detected for ${pageUser}`);

        //adding styles
        GM_addStyle('span.user-header__dislike-icon::before { content: "\uD83D\uDC4E"; }');


        let pageUserKey = key(pageUser);

        let today = new Date().toISOString().split('T')[0]; // Get the current date in ISO format

        //load or create the summary data for the user
        let summary = localStorage.getItem(pageUserKey);
        //If no summary data, we create it
        if (!summary) {
            summary = {
                user: pageUser,
                visits: 1,
                previousVisit: false,
                lastVisit: today,
                disliked: false
            };
        } else {
            summary = JSON.parse(summary);
        }

        //Increment the number of visits if the page has been visited not today
        if (summary.lastVisit !== today) {
            summary.visits += 1;
            summary.previousVisit = summary.lastVisit;
            summary.lastVisit = today;
            if (!summary.user) {
                summary.user = pageUser;
            }
        }

        //Store the summary
        console.debug(`User ${pageUser}:`, summary);
        localStorage.setItem(pageUserKey, JSON.stringify(summary));

        // header optimization ====================================================
        let actionsDiv = document.querySelector(`div.${userPageCSSPrefix}__actions`);

        //summary text
        let summaryElement = document.createElement('span');
        if (summary.previousVisit) {
            summaryElement.textContent = `- Visited ${summary.visits} times, last visit on ${summary.previousVisit}`;
        } else {
            summaryElement.textContent = `- First time visit`;
        }

        if (userPageURLMatch) {
            let d = document.createElement('div');
            d.appendChild(summaryElement);
            document.querySelector(`div.${userPageCSSPrefix}__info`).appendChild(d);
        } else {
            document.querySelector(`div.${userPageCSSPrefix}__published`).appendChild(summaryElement);
        }

        let dislikeButton = document.createElement('button');
        dislikeButton.type = 'button';
        dislikeButton.className = `${userPageCSSPrefix}__favourite`;

        let iconElement = document.createElement('span');
        iconElement.className = `${userPageCSSPrefix}__fav-icon`
        iconElement.textContent = summary.disliked ? '👍🏻' : '👎';
        dislikeButton.appendChild(iconElement);

        let textElement = document.createElement('span');
        textElement.className = `${userPageCSSPrefix}__fav-text`;
        textElement.textContent = summary.disliked ? 'Un-dislike' : 'Dislike';
        dislikeButton.appendChild(textElement);
        dislikeButton.addEventListener('click', () => {
            summary.disliked = !summary.disliked;
            localStorage.setItem(pageUserKey, JSON.stringify(summary));
            iconElement.textContent = summary.disliked ? '👍🏻' : '👎';
            textElement.textContent = summary.disliked ? 'Un-dislike' : 'Dislike';
        });

        actionsDiv.appendChild(dislikeButton);

        // footer optimization ======================================================================

        // bottom header repeater
        let navMenu = document.querySelector('nav.post__nav-links');
        let commentSection = document.querySelector('footer.post__footer');

        if (navMenu && commentSection) {
            commentSection.appendChild(navMenu.cloneNode(true));
        }

        // collect attachments
        if (userPostsPageURLMatch) {
            var headers = document.getElementsByTagName('h2');
            for (var i = 0; i < headers.length; i++) {
                console.log(headers[i]);
                if (headers[i].innerHTML == 'Downloads') {
                    let copyDownloadsButton = document.createElement('button');
                    copyDownloadsButton.type = 'button';
                    copyDownloadsButton.className = `${userPageCSSPrefix}__favourite`;

                    let textElement = document.createElement('span');
                    textElement.className = `${userPageCSSPrefix}__fav-text`;
                    textElement.textContent = 'Copy';
                    copyDownloadsButton.appendChild(textElement);
                    copyDownloadsButton.addEventListener('click', () => {
                        var attachemnts = '';
                        var c = 0;
                        document.querySelectorAll('a.post__attachment-link').forEach(l => {
                            attachemnts += l.href + '\n';
                            c++;
                        });
                        GM_setClipboard (attachemnts);
                        console.log('copied (', c, ')' );
                    });

                    headers[i].appendChild(copyDownloadsButton);
                }
            }
        }
    }

    // list pages
    if (artistsPageURLMatch) {
        console.debug('Artist list page detected');

        // Styles
        GM_addStyle('.user-card--dislike {border-color: hsl(0deg 100% 50%); border-style: solid; border-width: 2px; }');

        let enrichUserCard = function(c) {
            let summary = extractSummary(c.href);
            if (summary) {
                c.className += ' co-parsed';

                let serviceLabel = c.querySelector('span.user-card__service');
                let visitedLabel = serviceLabel.cloneNode(true);
                visitedLabel.textContent = `# ${summary.visits}`;
                visitedLabel.style.marginLeft = '4px';
                visitedLabel.style.backgroundColor = 'rgb(240, 140, 207)';
                serviceLabel.insertAdjacentElement('afterend', visitedLabel);

                if (summary.previousVisit) {
                    let daysAfterLabel = visitedLabel.cloneNode(true);
                    let daysFromLastVisit = Math.floor((new Date() - new Date(summary.previousVisit)) / (1000 * 60 * 60 * 24));
                    daysAfterLabel.textContent = `📅 ${daysFromLastVisit}`
                    daysAfterLabel.title = `Last visit on ${summary.previousVisit}`;
                    visitedLabel.insertAdjacentElement('afterend', daysAfterLabel);
                }

                if (summary.disliked) {
                    c.className += ' user-card--dislike';
                }
            }
        }

        // Callback function for the MutationObserver
        function mutationCallback(mutationsList, observer) {
            for (const mutation of mutationsList) {
                if (mutation.type === 'childList') {
                    // Process newly added user cards
                    mutation.addedNodes.forEach(node => {
                        if (node.tagName === 'A'
                            && node.classList.contains('user-card')
                            && !node.classList.contains('co-parsed')) {
                            enrichUserCard(node);
                        }
                    });
                }
            }
        }

        // Create a MutationObserver to watch for changes in the DOM
        const observer = new MutationObserver(mutationCallback);

        // Define the options for the observer
        const observerOptions = {
            childList: true,
            subtree: true
        };

        // Start observing the document body for changes
        observer.observe(document.body, observerOptions);
    }

    // post list pages
    if (postsPageURLMatch) {
        console.debug('Post list page detected');

        // Styles
        GM_addStyle('.post-card--dislike {border-color: hsl(0deg 100% 50%); border-style: solid; border-width: 2px; }');

        document.querySelectorAll('article.post-card').forEach(card => {
            let summary = extractSummary(card.querySelector('a').href, true);
            console.log(summary);
            if (summary) {
                card.className += ' co-parsed';

                //if there is a summary
                if (summary.user) {
                    console.log('sum', summary);
                    //replace attachment number with username
                    card.querySelector('footer > div').textContent = `${summary.user} (${summary.visits})`;

                    if (summary.disliked) {
                        card.className += ' post-card--dislike';
                    }
                } else {
                    card.querySelector('footer > div').textContent = summary;
                }
            }
        })
    }
})();