Camwhores.tv Utilities Mod

Info preview, removal of lock from friends' vidoes, restored upload button, and more.

Από την 07/08/2024. Δείτε την τελευταία έκδοση.

// ==UserScript==
// @name         Camwhores.tv Utilities Mod
// @namespace    https://sleazyfork.org/users/1281730-vipprograms
// @version      1.14.0
// @description  Info preview, removal of lock from friends' vidoes, restored upload button, and more.
// @author       vipprograms
// @match        https://www.camwhores.tv/*
// @exclude      *.camwhores.tv/*mode=async*
// @grant        GM_xmlhttpRequest
// @grant        GM.getValue
// @grant        GM_getValue
// @grant        GM.setValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM.registerMenuCommand
// @grant        GM_addStyle
// @grant        GM_download
// @grant        GM_openInTab
// @grant        window.close
// @icon         data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAMUExURQAAAP8ANwwA/////7gbQJkAAAABdFJOUwBA5thmAAAAAWJLR0QDEQxM8gAAAAd0SU1FB+gDHhIuCjXV/h8AAAA4SURBVAjXY2ANDQ1gEA0NDWEIYWBgZAhgAAIUghEiC1YHBhpMDRpIhBbXghUMXKtWLWBgWqHVAACjlwz/pN0YPwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyNC0wMy0zMFQxODo0NjowOSswMDowME+iXNIAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjQtMDMtMzBUMTg6NDY6MDkrMDA6MDA+/+RuAAAAKHRFWHRkYXRlOnRpbWVzdGFtcAAyMDI0LTAzLTMwVDE4OjQ2OjEwKzAwOjAwMNiA/AAAAABJRU5ErkJggg==
// @require      https://cdn.jsdelivr.net/npm/@violentmonkey/shortcut@1
// @require      https://code.jquery.com/jquery-3.7.1.slim.min.js
// ==/UserScript==

(function () {
    'use strict';


    let _GM_registerMenuCommand, _GM_notification, options, optionName;
    let backgroundChecksMinutes = 60;

    const { register } = VM.shortcut;
    const currentVersion = GM_info.script.version;
    const currentDate = new Date();
    const metaPage = "https://update.sleazyfork.org/scripts/491272/Camwhorestv%20Utilities%20Mod.meta.js";
    const capitalize = s => (s && s[0].toUpperCase() + s.slice(1)) || "";
    const isArrayEmpty = arr => !arr.length;
    const processedVideos = new Set();

    VM.shortcut.register('c-i', () => {
        console.log('You just pressed Ctrl-I');
    });

    if (typeof GM_registerMenuCommand !== 'undefined') {
        _GM_registerMenuCommand = GM_registerMenuCommand;
    } else if (typeof GM !== 'undefined' && typeof GM.registerMenuCommand !== 'undefined') {
        _GM_registerMenuCommand = GM.registerMenuCommand;
    } else {
        // _GM_registerMenuCommand = (s, f) => { debug(s); debug(f); };
        console.log("Oh no");
    }

    function saveValue(key, array) {
        GM.setValue(key, array).then(function () {
            console.log("Array saved successfully.");
        }).catch(function (error) {
            console.error("Error saving array:", error);
        });
    }

    function saveArraySettings(arrayName, optionName, currentValue) {
        let settings = retrieveValueFromStorage(arrayName) || {};
        settings[optionName] = currentValue;
        saveValue(arrayName, settings);
    }


    function retrieveValueFromStorage(key) {
        if (typeof GM_getValue === "function") {
            return GM_getValue(key, false);
        }
        if (typeof GM === "object" && typeof GM.getValue === "function") {
            return GM.getValue(key, false).then(value => value);
        }
        console.error("Unsupported userscript manager.");
        return undefined;
    }

    function toggleChange(optionName, defaultValue = false, negativeDont = false) { // ONLY for menu commands
        let options = retrieveValueFromStorage("options") || {};
        let currentState = options[optionName] !== undefined ? options[optionName] : defaultValue;

        let commandLabel = negativeDont ? (currentState ? `Don't ${optionName}` : capitalize(optionName))
            : (currentState ? `Disable ${optionName}` : `Enable ${optionName}`);

        _GM_registerMenuCommand(commandLabel, () => {
            options[optionName] = !currentState;
            saveValue("options", options);
            setTimeout(() => location.reload(), 500);
        });

        if (options[optionName] === undefined) {
            options[optionName] = defaultValue;
            saveValue("options", options);
        }
    }

    function createForm(array_name, delimitedString) {
        return new Promise((resolve, reject) => {
            const form = document.createElement('form');
            form.className = 'form-container';
            form.innerHTML = `
            <textarea class="textarea-input" autofocus>${delimitedString}</textarea>
            <div class="buttons-line">
                <button type="button" class="cancel-button">Cancel</button>
                <button type="submit" class="submit-button">Submit</button>
            </div>`;
            document.body.appendChild(form);

            const removeForm = (message) => {
                document.body.removeChild(form);
                reject(message);
            };

            form.querySelector('.cancel-button').onclick = () => removeForm('Form cancelled');
            window.onclick = (event) => !form.contains(event.target) && removeForm('Form cancelled');

            form.onsubmit = (event) => {
                event.preventDefault();
                const inputValue = form.querySelector('textarea').value.split('\n').map(line => line.trim()).filter(line => line);
                document.body.removeChild(form);
                resolve(inputValue);
            };
        });
    }

    function optionsArrayEditor(array_name, optionsKeyLegible) {

        _GM_registerMenuCommand("Change " + optionsKeyLegible[array_name], () => {
            var originalArray = retrieveValueFromStorage(array_name);
            var delimitedString = originalArray.join("\n");

            createForm(array_name, delimitedString).then(lines => {
                saveValue(array_name, lines);
            });

        });
    }

    function optionsStringEditor(string_name, optionsKeyLegible) {
        _GM_registerMenuCommand(`Change ${optionsKeyLegible[string_name]}`, async () => {
            const originalString = retrieveValueFromStorage(string_name);
            const lines = await createForm(string_name, originalString);
            saveValue(string_name, lines.join('\n'));
        });
    }

    let auto_replies_default = ["Next time you send me a request with 0 videos, I'll block you",
        "Very nice videos",
        "Why? What's wrong?",
        "Sorry, I don't like your videos",
        "You don't have any videos"];
    let highlight_keywords_default = ['joi', 'cei', 'fuck', "cumshot"];
    let friend_request_text_default = "Hi! I'm interested in this video:";
    let alternative_thumbnails_users = ['deathstar45', 'usualsuspekt', 'MrPussyGod', 'peacebitch', 'ADCGHN11123', 'Garbage Pile'];

    let auto_replies = retrieveValueFromStorage("auto_replies");
    let highlight_keywords = retrieveValueFromStorage("highlight_keywords");
    let friend_request_text = retrieveValueFromStorage("friend_request_text");

    if (!auto_replies) {
        auto_replies = auto_replies_default;
        saveValue("auto_replies", auto_replies_default);
    }
    if (!highlight_keywords) {
        highlight_keywords = highlight_keywords_default;
        saveValue("highlight_keywords", highlight_keywords_default);
    }
    if (!friend_request_text) {
        friend_request_text = friend_request_text_default;
        saveValue("friend_request_text", friend_request_text_default);
    }


    var optionsKeyLegible = {
        auto_replies: "template replies",
        highlight_keywords: "highlight keywords",
        friend_request_text: "friend requests text",
        alternative_thumbnails_users: "users with bad default thumbnails"
    }
    optionsArrayEditor("auto_replies", optionsKeyLegible)
    optionsArrayEditor("highlight_keywords", optionsKeyLegible)
    optionsStringEditor("friend_request_text", optionsKeyLegible)


    optionName = "restore upload button";
    toggleChange(optionName, true)
    var restoreUploadButton = retrieveValueFromStorage("options")[optionName];

    optionName = "notify me of processed videos";
    toggleChange(optionName, false)
    var notifyProcessedVideos = retrieveValueFromStorage("options")[optionName];

    let updateRemindersOptionName = "update reminders";
    toggleChange(updateRemindersOptionName, true)
    var updateReminders = retrieveValueFromStorage("options")[updateRemindersOptionName];

    optionName = "blur images";
    toggleChange(optionName, false, true); // name, default, don't as negative
    let blurImages = retrieveValueFromStorage("options")[optionName];



    const style = document.createElement('style');
    // custom CSS
    style.textContent = `a.button {color: dimgrey !important;}ul > li.next, ul > li.prev {display: list-item !important;}.item:hover > a > div.img > .friends-tag, .item:hover > a > div.img > .videos-tag {background-color: #1a1a1a !important;}.button {color: rgb(183, 176, 167);text-align: center;border: 1px solid transparent;font-size: 14px;padding: 5px 10px;cursor: pointer;background: linear-gradient(to bottom, #ffffff 0%, #cccccc 100%);border-radius: 3px;display: inline-block;margin: 0 .3rem .3rem 0;}.button:hover {color: #f56c08 !important;background: #1e1e1e;}div.img span.unread-notification {background: #c45606ab;backdrop-filter: blur(5px) brightness(1);top: 0;left: 0;border-bottom-left-radius: 3px;border-bottom-right-radius: 3px;outline: 3px solid #f56c08;animation: glow 5s infinite;}@keyframes glow {0% {outline-color: #f56c08d6;}50% {outline-color: transparent;}100% {outline-color: #f56c08d6;}}.form-container {box-sizing: border-box;position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);max-width: 100vw;width: 800px;padding: 2.5rem;border-radius: .25rem;z-index: 99999;background-color: transparent !important;backdrop-filter: blur(1rem) brightness(.8) contrast(1.2);}.form-container * {box-sizing: inherit;}.textarea-input {width: 100%;min-height: 10rem;padding: 1rem;border-radius: .25rem;color-scheme: dark !important;}.buttons-line {margin-top: .3rem;gap: .3rem;width: 100%;display: flex;}.submit-button, .cancel-button {transform: .3s;}.submit-button {height: 50px;flex-grow: 1;cursor: pointer;border-radius: .25rem;color-scheme: dark !important;font-weight: bold;border: 0;}.cancel-button {width: min-content;height: 50px;cursor: pointer;border-radius: .25rem;color-scheme: dark !important;background-color: red;font-weight: bold;padding-inline: .5rem;border: 0;}input.accept-button, span.accept-button {color: #fff;background-image: linear-gradient(rgb(0, 128, 0) 0%, rgb(0, 255, 0) 100%);}input.accept-button:hover, span.accept-button:hover {color: #fff !important;background-image: linear-gradient(rgb(34, 139, 34) 0%, rgb(50, 205, 50) 100%) !important;}input.reject-button, span.reject-button {color: #fff;background-image: linear-gradient(rgb(255, 0, 0) 0%, rgb(128, 0, 0) 100%);}input.reject-button:hover, span.reject-button:hover {color: #fff !important;background-image: linear-gradient(rgb(220, 20, 60) 0%, rgb(178, 34, 34) 100%) !important;}#vid_previews {display: flex;flex-wrap: nowrap;height: 135px;margin-block: .5rem;gap: 5px;box-sizing: border-box;}#vid_previews .img-div {width: 185px;height: 100%;position: relative;box-sizing: inherit;}.list-messages .added {position: absolute;right: 0;}.list-messages .item {margin-right: 0;}#vid_previews .img-div img {box-sizing: inherit;width: 100%;height: 100%;}#vid_previews .img-div h2 {position: absolute;bottom: 0;background-color: rgba(0, 0, 0, 0.65);font-size: 1rem;line-height: 1.2rem;backdrop-filter: blur(5px);color: rgb(196, 196, 196);width: 100%;word-wrap: break-word;box-sizing: inherit;text-align: center;}.bottom-element {margin-top: 1em;margin-bottom: 1em;}form .bottom {padding-top: .5rem;}form .bottom .submit {float: initial;height: auto;padding: .45rem .5rem;margin: 0;}.margin-fix .bottom {margin: 0;}#gen_joined_text {margin: .5rem 0 .25rem 0;}a[href="http://flowplayer.org"], a.fp-brand {opacity: 0 !important;pointer-events: none !important;}div.block-profile > div > div > div.about-me > div > em {white-space: pre;}h1.online:after {content: '';height: .45rem;aspect-ratio: 1;display: inline-block;background-color: green;position: relative;left: .5rem;border-radius: 50%;bottom: .1rem;}div.block-profile > div > div > div.about-me > div {width: 100%;box-sizing: border-box;}div.block-profile > div > div > div.about-me > div > em {width: 100%;display: inline-block;word-break: auto-phrase;word-wrap: break-word;box-sizing: border-box;text-wrap: pretty;}

   .user-search {float: right;position: relative;margin-left: 3px;cursor: pointer;background: linear-gradient(to bottom, #f1f1f1 0%, #d8d8d8 100%);border-radius: 2px;padding: 0;}
    .user-search strong {display: block;font-size: 12px;line-height: 15px;padding: 5px 12px 5px 28px;white-space: nowrap;color: #4e4e4e;cursor: pointer;max-width: 200px;overflow: hidden;}
    .user-search .type-search {background: url('https://shrph.altervista.org/img/search.png') 5px 4px no-repeat;}
    .user-search .icon {display: block;position: absolute;width: 100%;height: 100%;top: 0;}
    .user-search:hover {
    background: linear-gradient(to bottom, #e1e1e1 0%, #c8c8c8 100%);
}

.user-search:active {
    background: linear-gradient(to bottom, #d1d1d1 0%, #b8b8b8 100%);
}

.user-search:hover strong {
    color: #333;
}

.user-search:active strong {
    color: #111;
}

    #tab_screenshots > div > .item, #tab_screenshots > div > .item > .img {width: 250px;height: auto;}
    @keyframes fadeAnimation {0% {opacity: 1;}50% {opacity: .3;}100% {opacity: 1;}}.fade {animation: fadeAnimation 1s infinite;}.switch {position: relative;display: inline-block;width: 30px;height: 17px;}.switch input {opacity: 0;width: 0;height: 0;}.slider {position: absolute;cursor: pointer;top: 0;left: 0;right: 0;bottom: 0;background-color: #ccc;transition: .4s;}.slider:before {position: absolute;content: "";height: 13px;width: 13px;left: 2px;bottom: 2px;background-color: white;transition: .4s;}input:checked + .slider {background-color: #f56c08;}input:focus + .slider {box-shadow: 0 0 1px #F28022;}input:checked + .slider:before {transform: translateX(13px);}.slider.round {border-radius: 17px;}.slider.round:before {border-radius: 50%;}.list-comments .dim-comment {background-color: #9e1e1e2b;background-image: none;}.list-comments .dim-comment p {opacity: 1;}
`;
    // old:

    //  style.textContent = `a.button{color:#4e4e4e !important;}ul > li.next, ul > li.prev {display: list-item !important;}.item:hover > a > div.img > .friends-tag, .item:hover > a > div.img > .videos-tag{background-color:#1a1a1a !important;}.button{color: rgb(183, 176, 167);text-align: center;border: 1px solid transparent;font-size: 14px;padding: 5px 10px;cursor: pointer;background: linear-gradient(to bottom, #ffffff 0%, #cccccc 100%);border-radius: 3px;display: inline-block;margin: 0 .3rem .3rem 0;color: dimgrey;}.button:hover{color: #f56c08 !important;border: 1px solid transparent;background: #1e1e1e;}div.img span.unread-notification{background: #c45606ab;backdrop-filter: blur(5px) brightness(1);top: 0px;left: 0px;border-bottom-left-radius: 3px;border-bottom-right-radius: 3px;outline: 3px solid #f56c08;animation: glow 5s infinite;}@keyframes glow {0% {outline-color: #f56c08d6;}50% {outline-color: transparent;}100% {outline-color: #f56c08d6;}}.form-container {box-sizing: border-box;position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);max-width: 100vw;width: 800px;padding: 2.5rem;border-radius: .25rem;z-index:99999;background-color: transparent !important;backdrop-filter: blur(1rem) brightness(.8) contrast(1.2) }.form-container * {box-sizing: inherit;}.textarea-input {width: 100%;min-height: 10rem;padding: 1rem;border-radius: .25rem;color-scheme: dark !important;}.buttons-line {margin-top: .3rem;gap:.3rem;width: 100%;display: flex;}.submit-button:hover, .cancel-button:hover{filter: brightness(.8) contrast(1.2) }.submit-button, .cancel-button{transform: .3s;}.submit-button {height: 50px;flex-grow: 1;cursor: pointer;border-radius: .25rem;color-scheme: dark !important;font-weight:bold;border:0;}.cancel-button {width:min-content;height: 50px;cursor: pointer;border-radius: .25rem;color-scheme: dark !important;background-color: red;font-weight:bold;padding-inline:.5rem;border:0;}input.accept-button, span.accept-button {color: #fff;background-image: linear-gradient(rgb(0, 128, 0) 0%, rgb(0, 255, 0) 100%);}input.accept-button:hover, span.accept-button:hover {color: #fff !important;background-image: linear-gradient(rgb(34, 139, 34) 0%, rgb(50, 205, 50) 100%) !important;}input.reject-button, span.reject-button {color: #fff;background-image: linear-gradient(rgb(255, 0, 0) 0%, rgb(128, 0, 0) 100%);}input.reject-button:hover, span.reject-button:hover {color: #fff !important;background-image: linear-gradient(rgb(220, 20, 60) 0%, rgb(178, 34, 34) 100%) !important;}#vid_previews {display: flex;flex-wrap: nowrap;height: 135px;margin-block: .5rem;gap: 5px;box-sizing: border-box;}#vid_previews .img-div {width:185px;height:100%;position: relative;box-sizing: inherit;}.list-messages .added {position: absolute;right: 0;}.list-messages .item {margin-right: 0;}#vid_previews .img-div img{box-sizing: inherit;width: 100%;height:100%;}#vid_previews .img-div h2{position: absolute;bottom: 0;background-color: rgb(0 0 0 / 65%);font-size: 1rem;line-height: 1.2rem;backdrop-filter: blur(5px);color: rgb(196 196 196);width: 100%;word-wrap: break-word;box-sizing: inherit;text-align: center;}.bottom-element{margin-top: 1em;margin-bottom: 1em;}form .bottom{padding-top: .5rem;}form .bottom .submit{float: initial;height: auto;padding: .45rem .5rem;margin: 0;}.margin-fix .bottom{margin:0;}#gen_joined_text{margin: .5rem 0 .25rem 0;}a[href="http://flowplayer.org"], a[class="fp-brand"]{opacity: 0 !important;pointer-events: none !important;}div.block-profile > div > div > div.about-me > div > em{white-space:pre;}h1.online:after{content: '';height: .45rem;aspect-ratio: 1;display: inline-block;background-color: green;position: relative;left: .5rem;border-radius: 50%;bottom: .1rem;}div.block-profile > div > div > div.about-me > div{width: 100%;box-sizing: border-box;}div.block-profile > div > div > div.about-me > div>em{width: 100%;display: inline-block;word-break: auto-phrase;word-wrap: break-word;box-sizing: border-box;text-wrap: pretty;}.user-search {float: right;position: relative;margin-left: 3px;cursor: pointer;background: #f1f1f1;background: linear-gradient(to bottom, #f1f1f1 0%, #d8d8d8 100%);border-radius: 2px;padding:0;}.user-search strong {display: block;font-size: 12px;line-height: 15px;padding: 5px 12px 5px 28px;white-space: nowrap;color: #4e4e4e;cursor: pointer;max-width: 200px;overflow: hidden;}.user-search .type-search {background: url('https://shrph.altervista.org/img/search.png') 5px 4px no-repeat;}.user-search .icon {display: block;position: absolute;width: 100%;height: 100%;top:0;}#tab_screenshots > div > .item, #tab_screenshots > div > .item > .img {width: 250px;height: auto;}#tab_screenshots > div > .item > .img {}@keyframes fadeAnimation {0% {opacity: 1;}50% {opacity: .3;}100% {opacity: 1;}}.fade {animation: fadeAnimation 1s infinite;}.switch {position: relative;display: inline-block;width: 30px;height: 17px;}.switch input {opacity: 0;width: 0;height: 0;}.slider {position: absolute;cursor: pointer;top: 0;left: 0;right: 0;bottom: 0;background-color: #ccc;-webkit-transition: .4s;transition: .4s;}.slider:before {position: absolute;content: "";height: 13px;width: 13px;left: 2px;bottom: 2px;background-color: white;-webkit-transition: .4s;transition: .4s;}input:checked + .slider {background-color: #f56c08;}input:focus + .slider {box-shadow: 0 0 1px #F28022;}input:checked + .slider:before {-webkit-transform: translateX(13px);transform: translateX(13px);}.slider.round {border-radius: 17px;}.slider.round:before {border-radius: 50%;}.list-comments .dim-comment p {opacity: 1;}.list-comments .dim-comment {background-color: #9e1e1e2b;background-image: none;}
    //  `;
    document.head.appendChild(style);


    function isNotPage(...pages) {
        const currentUrl = window.location.href.split(/[?#]/)[0];
        return !pages.includes(currentUrl);
    }

    function makeValidUrlString(inputString) {
        var validString = inputString.replace(/[^\w\s\/-]/g, ''); // Remove special characters
        validString = validString.replace(/[-_]{2,}/g, '-'); // Turn consecutive underscores or hyphens into single hyphens
        validString = validString.replace(/\s+/g, '-'); // Turn spaces into hyphens
        validString = validString.replace(/\//g, '-'); // Turn slashes into hyphens
        return validString;
    }

    function friendRequestSetup(video_title, video_url, userID) {
        var vid_code = video_url.replace("https://www.camwhores.tv/videos/", "");
        var parts = vid_code.split("/");
        var videoId = parts[0];
        var code = parts[1];
        let cumData = {
            title: video_title,
            videoId: videoId,
            userId: userID
        };

        let lastCumVideoAdd = JSON.parse(localStorage.getItem('last_cum_video_data')) || {};
        lastCumVideoAdd[userID] = cumData;
        localStorage.setItem('last_cum_video_data', JSON.stringify(lastCumVideoAdd));

    }

    function convertToSeconds(timeString) {
        const [_, value, unit] = timeString.match(/(\d+) (\w+)/);
        const conversion = {
            second: 1,
            seconds: 1,
            minute: 60,
            minutes: 60,
            hour: 3600,
            hours: 3600,
            day: 86400,
            days: 86400,
            week: 604800,
            weeks: 604800,
            month: 2592000, // 30 days
            months: 2592000, // 30 days
            year: 31536000, // 365 days
            years: 31536000 // 365 days
        };
        return value * (conversion[unit] || 0);
    }

    function fetchAndParseHTML(url, callback) {
        GM_xmlhttpRequest({
            method: "GET",
            url: url,
            onload: function (response) {
                var parser = new DOMParser();
                var htmlDoc = parser.parseFromString(response.responseText, "text/html");
                callback(htmlDoc);
            }
        });
    }

    function fetchAndExtract(url, callback) {
        GM_xmlhttpRequest({
            method: "GET",
            url: url,
            onload: function (response) {
                document.evaluate('//*[@id="list_messages_my_conversation_messages_items"]/div/div[3]/img', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue?.remove();
                const htmlDoc = new DOMParser().parseFromString(response.responseText, "text/html");
                callback(htmlDoc);
            }
        });
    }

    function getAllSessionStorage() {
        const sessionStorageItems = {};
        for (let i = 0; i < sessionStorage.length; i++) {
            const key = sessionStorage.key(i);
            sessionStorageItems[key] = sessionStorage.getItem(key);
        }
        return sessionStorageItems;
    }

    function getCurrentPage() {
        const allSessionStorage = getAllSessionStorage();
        const paramsStorageKey = Object.keys(allSessionStorage).find(key => key.endsWith(":params"));
        if (!paramsStorageKey) return 1;

        const paramsValue = JSON.parse(allSessionStorage[paramsStorageKey])?.from_my_conversations;
        return parseInt(paramsValue, 10) || 1;
    }

    function setUploadButtonTimestamp() {
        localStorage.setItem('UploadButtonTimestamp', JSON.stringify(Date.now()));
    }

    function getCookie(name) {
        let cookieArr = document.cookie.split("; ");
        for (let cookie of cookieArr) {
            let [key, value] = cookie.split("=");
            if (key === name) return value;
        }
        return null;
    }

    function loadArrayLocalStorage(name = 'lastVideoData') {
        var jsonData = localStorage.getItem(name);
        if (!jsonData) {
            return [];
        }

        var data = JSON.parse(jsonData);
        return data;
    }

    function saveArrayLocalStorage(name='lastVideoData',data) {
        var timestamp = Date.now();
        var jsonData = JSON.stringify(data);
        localStorage.setItem(name, jsonData);
    }

    function secondsSinceLocalStorage(name) {
        const storedItem = JSON.parse(localStorage.getItem(name));
        return storedItem ? (Date.now() - storedItem.timestamp) / 1000 : null;
    }

    function getNewUID() {
        const sessionStorageData = getAllSessionStorage();
        if (!sessionStorageData) return null;

        for (const key in sessionStorageData) {
            const match = key.match(/^(\d+):https:\/\/www\.camwhores\.tv/);
            if (match) return match[1];
        }
        return null;
    }

    function saveUID() {
        const uid = getNewUID();
        if (uid) {
            const current_kt = getCookie("kt_member");
            saveArrayLocalStorage("cum_user_id", { uid, kt: current_kt });
            return uid;
        }
        return null;
    }

    function silentUidUpdater() {
        const uidArray = loadArrayLocalStorage("cum_user_id");
        if (!uidArray || getCookie("kt_member") !== uidArray.kt) {
            saveUID();
        }
    }

    function currentUserID() {
        const current_kt = getCookie("kt_member");
        const uidArray = loadArrayLocalStorage("cum_user_id");

        return uidArray && uidArray.kt === current_kt ? uidArray.uid : saveUID();
    }

    async function getResponseStatus(url) {
        try {
            const response = await fetch(url, { method: 'HEAD' });
            return response.status;
        } catch {
            return null;
        }
    }

    function secondsSinceTimestamp(timestamp) {
        return (Date.now() - timestamp) / 1000;
    }

    const currentUID = currentUserID();
    // console.log("User ID:", currentUID, "(https://www.camwhores.tv/members/" + currentUID + "/)")

    silentUidUpdater();

    function uploadRestored() {
        alert("The upload button has been restored!");
        localStorage.removeItem("UploadButtonTimestamp");
    }


    async function handleUploadButton() {
        const nav = document.querySelector('.navigation .primary');
        const lastCheck = localStorage.getItem("UploadButtonTimestamp");
        const minutesSinceLastCheck = lastCheck ? secondsSinceTimestamp(lastCheck) / 60 : null;

        if (nav && restoreUploadButton && ![...nav.querySelectorAll('a')].some(link => link.textContent.trim() === 'Upload')) {
            const uploadLink = document.createElement('a');
            uploadLink.style.cssText = 'background-color: #215521; display: block; padding: 11px 0; cursor: pointer;';
            uploadLink.textContent = 'UPLOAD';
            uploadLink.href = '/upload-video/';
            nav.appendChild(uploadLink);

            uploadLink.addEventListener('click', async (event) => {
                event.preventDefault();
                const status = await getResponseStatus('/upload-video/');
                if (status === 200) {
                    window.location.href = '/upload-video/';
                } else if (confirm(`Upload functionality is disabled (${status}). Notify me when available?`)) {
                    setUploadButtonTimestamp();
                }
            });

            if (lastCheck && minutesSinceLastCheck > 30) {
                const status = await getResponseStatus('/upload-video/');
                status === 200 ? uploadRestored() : (console.log(`Upload button still unavailable. Last check ${Math.floor(minutesSinceLastCheck)} minutes ago`), setUploadButtonTimestamp());
            }
        } else if (lastCheck) {
            uploadRestored();
        }
    }

    handleUploadButton();



    if (window.location.href.endsWith("?action=reject_add_to_friends_done") ||
        window.location.href.endsWith("?action=confirm_add_to_friends_done")) {

        const urlOfUser = document.querySelector('#list_messages_my_conversation_messages > div.headline > h2 > a:nth-child(2)');
        const username = urlOfUser.innerHTML.trim();
        const userID = urlOfUser.getAttribute('href').match(/\/(\d+)\//)[1];

        const storedData = JSON.parse(localStorage.getItem('closedWindow'));
        if (storedData) {
            const { uid, timestamp } = storedData;
            const timeDifference = (Date.now() - timestamp) / 1000; // in seconds
            if (timeDifference < 30 && userID === uid) {
                console.log(`Less than 30 seconds have passed since window closed. User ID: ${uid}`);
                window.close();
            }
        }
    }








    if (window.location.href == "https://www.camwhores.tv/my/messages/") {

      document.addEventListener('visibilitychange', function() {
          if (document.visibilityState === 'visible') {
              window.location.reload();
          }
      });


        function myMessagesAsyncURL(pageInt = 1) {
            const utcTimestamp = new Date().getTime();
            const pageID = pageInt.toString();
            return "https://www.camwhores.tv/my/messages/?mode=async&function=get_block&block_id=list_members_my_conversations&sort_by=added_date&from_my_conversations=" + pageID + "&_=" + utcTimestamp;
        }
        // console.log(myMessagesAsyncURL(3))






        function messagePageInfo(pageN) {
            let urlToPage = myMessagesAsyncURL(pageN);
            // console.log(urlToPage)
            return new Promise((resolve, reject) => {
                fetchAndExtract(urlToPage, function (htmlDoc) {
                    const conversations = [];
                    const items = htmlDoc.querySelectorAll("#list_members_my_conversations_items .item");
                    // console.log("htmlDoc:",htmlDoc)
                    items.forEach(item => {
                        const name = item.querySelector('a').getAttribute('title');
                        const unread = item.classList.contains('unread');
                        conversations.push({ name, unread });
                    });
                    resolve(conversations);
                });
            });
        }

        async function hasUnreadMessage(currentPage) {
            const data = await messagePageInfo(currentPage);
            return data.some(message => message.unread);
        }

        async function delay(ms) {
            return new Promise(resolve => setTimeout(resolve, ms));
        }

async function lastUnreadPage() {
    let currentPage = 1;
    let hasUnread = true;
    let lastUnreadPage = 0;
    let pagesChecked = 0;

    while (pagesChecked < 10 || hasUnread) {
        const data = await messagePageInfo(currentPage);
        hasUnread = data.some(message => message.unread);

        if (hasUnread) {
            lastUnreadPage = currentPage;
        }

        pagesChecked++;
        currentPage++;
        await delay(75);
    }

    return lastUnreadPage;
}





        async function checkAndFixMessages() {
            const currentPage = getCurrentPage();
            const data = await messagePageInfo(currentPage);
            // console.log(data);

            let hasNewElements = false;

            data.forEach(item => {
                const userItem = document.querySelector(`#list_members_my_conversations_items > div.item > a[title="${item.name}"]`);
                if (userItem) {
                    const unreadNotification = userItem.querySelector('div.img .unread-notification');
                    if (unreadNotification && !item.unread) {
                        unreadNotification.remove();
                        console.log("Message with", item.name, "was read");
                    }
                } else {
                    hasNewElements = true;
                }
            });

            if (hasNewElements) {
                location.reload();
            }
        }









        function processThumbnails() {
            // console.log("Processing thumbnails");
            var thumbnails = document.querySelectorAll('strong.title');
            if (thumbnails.length === 0) {
                console.log("Thumbnails not found. Retrying in 1000 milliseconds.");
                setTimeout(function () {
                    processThumbnails(); // Retry processing thumbnails
                }, 1000);
                return false; // Return false if thumbnails not found
            }

            thumbnails.forEach(function (thumbnail) {
                var usernameText = thumbnail.textContent.trim();
                if (usernameText) {
                    var usernameLink = document.querySelector('a[title="' + usernameText + '"]');
                    // console.log("usernameLink:",usernameLink)
                    if (usernameLink) {
                        var href = usernameLink.getAttribute('href');
                        var userIdMatch = href.match(/\/(\d+)\/$/);
                        if (userIdMatch && userIdMatch.length > 1) {
                            var userId = userIdMatch[1];
                            var usernameUrl = "https://www.camwhores.tv/members/" + userId + "/";
                            GM_xmlhttpRequest({
                                method: "GET",
                                url: usernameUrl,
                                onload: function (response) {
                                    var parser = new DOMParser();
                                    var htmlDoc = parser.parseFromString(response.responseText, "text/html");
                                    var infoMessage = htmlDoc.querySelector('.info-message');
                                    var usernameLink = document.querySelector('a[title="' + usernameText + '"]');
                                    // console.log(usernameText + ": " + usernameUrl)
                                    // console.log(usernameLink)
                                    if (infoMessage && infoMessage.textContent.includes("is in your friends list.")) {
                                        if (usernameLink) {
                                            var divElement = document.createElement('div');
                                            divElement.classList.add("friends-tag");
                                            divElement.textContent = "Friends ✅";
                                            divElement.style.position = "absolute";
                                            divElement.style.top = "0";
                                            divElement.style.right = "0";
                                            divElement.style.backgroundColor = "#414141"; //#1a1a1a
                                            divElement.style.borderBottomLeftRadius = "3px";
                                            divElement.style.color = "white";
                                            divElement.style.padding = "3px 5px";

                                            if (usernameLink) {
                                                var firstDivElement = usernameLink.querySelector('div.img');
                                                if (firstDivElement) {
                                                    firstDivElement.appendChild(divElement);
                                                }
                                            }
                                        }
                                    }
                                    if (infoMessage && infoMessage.textContent.includes("wants to be your friend.")) {
                                        divElement = document.createElement('div');
                                        divElement.textContent = "👀";
                                        divElement.title = "Wants to be your friend!";
                                        divElement.style.position = "absolute";
                                        divElement.style.bottom = "0";
                                        divElement.style.left = "0";
                                        // divElement.style.backgroundColor = "aquamarine";
                                        divElement.style.color = "white";
                                        divElement.style.fontSize = "1rem";
                                        divElement.style.padding = ".5rem .25rem";

                                        if (usernameLink) {
                                            firstDivElement = usernameLink.querySelector('div.img');
                                            if (firstDivElement) {
                                                firstDivElement.appendChild(divElement);
                                            }
                                        }
                                    }

                                    var titleVideosElement = htmlDoc.evaluate('//*[@id="list_videos_uploaded_videos"]/div[1]/h2', htmlDoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
                                    var videoNumber = 0;

                                    if (titleVideosElement && titleVideosElement.singleNodeValue) {
                                        var titleVideosText = titleVideosElement.singleNodeValue.textContent;
                                        if (titleVideosText) {
                                            var matches = titleVideosText.match(/\(([^)]+)\)/);
                                            videoNumber = matches ? matches[1] : 0;
                                        }
                                    }

                                    if (videoNumber || videoNumber === 0) {
                                        divElement = document.createElement('div');
                                        divElement.classList.add("videos-tag");
                                        divElement.textContent = videoNumber;
                                        divElement.style.position = "absolute";
                                        divElement.style.bottom = "0";
                                        divElement.style.right = "0";
                                        divElement.style.backgroundColor = "CadetBlue";
                                        divElement.style.color = "white";
                                        divElement.style.padding = "3px 5px";
                                        divElement.style.borderTopLeftRadius = "3px";
                                    }


                                    if (usernameLink) {
                                        firstDivElement = usernameLink.querySelector('div.img');
                                        if (firstDivElement) {
                                            firstDivElement.appendChild(divElement);
                                        }
                                    }
                                } // end httpGET reponse

                            });
                        } else {
                            console.log("User ID not found in href for:", usernameText);
                        }
                    } else {
                        console.log("Username link not found for:", usernameText);
                    }
                }




            });
            return true; // Return true if thumbnails processed successfully
        }

        function addButtonEventListener() {
            // console.log("Adding event listener to buttons");
            document.addEventListener('click', function (event) {
                var target = event.target;
                if (target && target.matches('a[data-action="ajax"]')) {
                    // console.log("Button clicked");

                    var h2Element = document.querySelector('#list_members_my_conversations div.headline h2');
                    const initialText = h2Element.textContent.trim();
                    // console.log("Initial text: " + initialText);
                    const checkChangeInterval = setInterval(function () {
                        h2Element = document.querySelector('#list_members_my_conversations div.headline h2');
                        const currentText = h2Element.textContent.trim();
                        // console.log("Current text: " + currentText);
                        if (currentText !== initialText) {
                            clearInterval(checkChangeInterval);
                            processThumbnails();
                        }
                    }, 100);
                }
            });
        }

        setTimeout(function () {
            processThumbnails();
            var thumbnailsProcessed = processThumbnails();
            if (!thumbnailsProcessed) {
                console.log("Processing thumbnails failed. Retrying in 1000 milliseconds.");
                setTimeout(function () {
                    processThumbnails(); // Retry processing thumbnails
                }, 1000);
            }
            addButtonEventListener();
        }, 1000);

        setInterval(function () {
            processThumbnails()
            silentUidUpdater()
            checkAndFixMessages()
        }, 5000);

        setInterval(function () {
            addButtons()
        }, 500);


        function addButtons() {
            const h2Element = document.querySelector('#list_members_my_conversations .headline h2');
            if (!h2Element) return console.log('h2Element not found');

            if (!document.getElementById('refreshIcon')) {
                const refreshIcon = document.createElement('span');
                refreshIcon.id = 'refreshIcon';
                refreshIcon.innerHTML = '&#8635;';
                refreshIcon.title = 'Refresh Utilities Mod';
                refreshIcon.style = 'cursor:pointer;margin-left:10px;';
                refreshIcon.addEventListener('click', () => {
                    processThumbnails();
                    console.log('Refreshing...');
                });
                h2Element.appendChild(refreshIcon);
            }

            if (!document.getElementById('lastUnreadButton')) {
                const lastUnreadButton = document.createElement('a');
                lastUnreadButton.id = 'lastUnreadButton';
                lastUnreadButton.innerHTML = '⏭';
                lastUnreadButton.title = 'Go to oldest unread message';
                lastUnreadButton.style = 'cursor:pointer;margin-left:10px;text-decoration:none;';
                lastUnreadButton.addEventListener('click', async () => {
                    lastUnreadButton.classList.add('fade');
                    const oldestUnreadPage = await lastUnreadPage();
                    console.log(oldestUnreadPage);
                    const linkElement = document.querySelector(`a[href="/my/messages/${oldestUnreadPage}/"][data-block-id="list_members_my_conversations"]`);
                    if (linkElement) {
                        linkElement.click();
                    } else {
                        console.log(`Element for page ${oldestUnreadPage} not found`);
                    }
                    lastUnreadButton.classList.remove('fade');

                });
                h2Element.appendChild(lastUnreadButton);
            }
        }

        addButtons()



    }
    if (window.location.href.startsWith("https://www.camwhores.tv/my/messages/") && window.location.href !== "https://www.camwhores.tv/my/messages/") {


        function updateButtonStyle(button, state) {
            button?.classList.add(state ? "accept-button" : "reject-button");
        }


        let textarea = document.querySelector("#send_message_message");
        let divTextarea = document.createElement('div');
        let urlOfUser = document.querySelector('#list_messages_my_conversation_messages > div.headline > h2 > a:nth-child(2)')
        let username = urlOfUser.innerHTML.trim();
        let userID = urlOfUser.getAttribute('href').match(/\/(\d+)\//)[1];
        let confirmButton = document.querySelector('input[name="confirm"]');
        let rejectButton = document.querySelector('input[name="reject"]');
        let buttonsParent = confirmButton ? confirmButton.parentElement : null;
        let confirmClose = null
        let denyClose = null

        if (sessionStorage.getItem('continue') === 'true') {
            sessionStorage.removeItem('continue');
            if (history.length === 1) window.location.href = `https://www.camwhores.tv/members/${userID}/#list_videos_my_uploaded_videos`;
        }

        confirmButton?.addEventListener('click', () => sessionStorage.setItem('continue', 'true'));

        document.querySelector('input.submit')?.addEventListener('click', () => divTextarea.innerHTML = '');

        $(document).ready(() => {
            $(document).keyup(event => {
                if (event.ctrlKey && event.key === 'Enter') {
                    event.preventDefault();
                    $("#send_message_form").submit();
                }
            });
        });


        function copyContentToTextarea() {
            const content = Array.from(divTextarea.childNodes).map(node =>
                node.nodeType === 3 ? node.textContent : node.tagName === 'IMG' ? node.alt : ''
            ).join('').trim();

            textarea.value = content;
        }



        textarea.style.display = "none";
        Object.assign(divTextarea.style, {
            display: "block",
            minHeight: "5rem",
            resize: "vertical",
            overflow: "auto",
            fontSize: "14px"
        });
        divTextarea.classList.add("textarea");
        divTextarea.contentEditable = true;
        textarea.parentNode.insertBefore(divTextarea, textarea.nextSibling);

        document.querySelectorAll('.smileys-bar img').forEach(img =>
            img.onclick = e => {
                e.preventDefault();
                e.stopPropagation();
                divTextarea.appendChild(Object.assign(img.cloneNode(true), { style: { cursor: "auto" } }));
                copyContentToTextarea();
            }
        );

        divTextarea.oninput = copyContentToTextarea;


        document.querySelectorAll('span.inline-text, span.original-text').forEach(function(span) {
            var lineText = span.textContent.trim();
            if (lineText.includes('##')) {
                var index = lineText.indexOf(':');
                var videoText = lineText.substring(0, index).trim();
                var videoInfo = lineText.substring(index + 1).trim().replace('##', '');
                let parts = videoInfo.split("\n");
                let videoCode = parts[0];
                let videoID = parts[1];
                let url = 'https://www.camwhores.tv/videos/' + videoID + "/" + makeValidUrlString(videoCode) + '/';
                // console.log(url)

                  fetch(url)
              .then(response => {
                if (response.ok) {
                  span.innerHTML = videoText + `<a href="` + url + '" class="toggle-button" style="display:inline-block">'+videoCode+'</a>';
                } else if (response.status === 404) {
                  console.log("URL is 404");
                } else {
                  console.error("Error:", response.status);
                }
              })
              .catch(error => console.error("Error:", error));

                // span.innerHTML = videoText + ': <a href="' + url + '" class="toggle-button" style="display:inline-block">Open video</a>';
            }
        });








        function andClosePage() {
            localStorage.setItem('closedWindow', JSON.stringify({ uid: userID, timestamp: Date.now() }));
            window.close();
        }

        function addCloseButtonsListeners(confirmButton, rejectButton) {
            if (!confirmButton) return {};

            const createButton = (text, action) => {
                const btn = document.createElement('span');
                btn.className = 'submit button';
                btn.textContent = text;
                btn.addEventListener('click', event => {
                    event.preventDefault();
                    action();
                    andClosePage();
                });
                return btn;
            };

            const buttonsParent = confirmButton.parentElement;
            const confirmClose = createButton('Confirm & Close', () => confirmButton.click());
            const denyClose = createButton('Deny & Close', () => rejectButton.click());

            buttonsParent.append(confirmClose, ' ', denyClose);
            confirmButton.style.width = `${confirmClose.offsetWidth}px`;
            rejectButton.style.width = `${denyClose.offsetWidth}px`;

            return { confirmClose, denyClose };
        }





function updateMessageDOM(htmlDoc, confirmClose, denyClose) {
    const twoYearsSeconds = 60 * 60 * 24 * 365 * 2;
    var titleText = htmlDoc.querySelector('#list_videos_uploaded_videos > div:nth-child(1) > h2')?.textContent.trim();
    var lastUploadTime = htmlDoc.querySelector('#list_videos_uploaded_videos_items > div.item > a > div.wrap > div.added > em')?.textContent.trim();

    var joinedDateText = htmlDoc.querySelector('div.block-profile > div.profile-list > div.margin-fix > div.column > div:nth-child(3) > em')?.textContent.trim();
    var lastLogin = htmlDoc.querySelector('div.block-profile > div.profile-list > div.margin-fix > div.last-login > div.item')?.textContent.trim();
    let secondsSinceOnline = convertToSeconds(lastLogin);
    let recentlyOnline = secondsSinceOnline <= 300;
    // let recentlyOnline = true;
    const headlineH2 = document.querySelector('#list_messages_my_conversation_messages .headline h2');
    console.log("recentlyOnline:", recentlyOnline);
    var recentlyUploaded = false;
    console.log("Has focus:",document.hasFocus(),"titleText:",titleText)
    if(!document.hasFocus() && !titleText){
      denyClose.click()
      window.close();
    }

    if(lastUploadTime){
      let lastUploadTimeSeconds = convertToSeconds(lastUploadTime);
      recentlyUploaded = lastUploadTimeSeconds < twoYearsSeconds;
    }

    if(recentlyOnline){
      GM_addStyle(`.list-messages .item.new .added:after {
          background-color: green;
        }`);

      document.querySelector("#list_messages_my_conversation_messages_items > div.item.new > div.added").textContent += " (online)";
      headlineH2.innerHTML += " (online)"

    }

    if (titleText) { // THERE ARE VIDEOS
        var extractedTitle = titleText.match(/\(([^)]+)\)/);
        if (extractedTitle && extractedTitle.length >= 1) {
            // THERE ARE VIDEOS

            if(recentlyUploaded){
                updateButtonStyle(confirmButton, true);
                updateButtonStyle(confirmClose, true);
            }else{
                updateButtonStyle(rejectButton, false);
                updateButtonStyle(denyClose, false);
            }

            var generatedTextDiv = document.createElement('div');
            generatedTextDiv.id = 'gen_joined_text';
            generatedTextDiv.innerHTML = `${extractedTitle[1]} videos, last ${lastUploadTime}<br>Joined ${joinedDateText}`;
            var messageDiv = document.querySelector('#list_messages_my_conversation_messages_items > div > div:nth-child(3)');
            if (messageDiv) {
                messageDiv.appendChild(generatedTextDiv);
            }
        }else{
          if(extractedTitle) console.log("extractedTitle:",extractedTitle)
        }


              if(confirmClose){
                    let imgData = [];
                    for (let i = 1; i <= 5; i++) {
                        let xpath = `//*[@id="list_videos_uploaded_videos_items"]/div[${i}]/a/div[@class="img"]/img`;
                        let img = htmlDoc.evaluate(xpath, htmlDoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
                        if (img) {
                            let parentAnchor = img.parentNode.parentNode;
                            imgData.push({ src: img.getAttribute('data-original'), alt: img.alt, url: parentAnchor.getAttribute('href') });
                        }
                    }
                        let vidPreviewsDiv = document.createElement("div");
                        vidPreviewsDiv.id = "vid_previews";
                        if (!isArrayEmpty(imgData)) {
                            imgData.forEach(data => {
                                let imgDiv = document.createElement("div");
                                imgDiv.classList.add("img-div");
                                let imgLink = document.createElement("a"); // New line
                                imgLink.href = data.url+"?please_check_friend"; // New line
                                let img = document.createElement("img");
                                img.src = data.src;
                                img.alt = data.alt;
                                img.title = data.alt;
                                let title = document.createElement("h2");
                                title.textContent = data.alt;
                                imgLink.appendChild(img); // Updated line
                                imgDiv.appendChild(imgLink); // Updated line
                                imgDiv.appendChild(title);

                                vidPreviewsDiv.appendChild(imgDiv);
                            });

                            buttonsParent.appendChild(vidPreviewsDiv);
                        }

                }


    } else {
        // THERE ARE NO VIDEOS
        console.log('No videos? Title: '+titleText+", content: "+lastUploadTime);
        updateButtonStyle(rejectButton, false);
        updateButtonStyle(denyClose, false);

        var messageDiv = document.querySelector('#list_messages_my_conversation_messages_items > div > div:nth-child(3)');
        if (messageDiv) {
            var generatedTextDiv = document.createElement('div');
            generatedTextDiv.id = 'gen_joined_text';
            generatedTextDiv.innerHTML = `Joined ${joinedDateText}<br><b>No videos</b>`;
            messageDiv.appendChild(generatedTextDiv);

        }
    }

}


        const bottomDiv = document.querySelector("#send_message_form > div > div.bottom");
        const responsesDiv = document.createElement("div");
        responsesDiv.id = "responses";
        responsesDiv.style.marginTop = "1rem";
        bottomDiv.insertAdjacentElement('afterend', responsesDiv);

        auto_replies.forEach(string => {
            const button = document.createElement("button");
            button.textContent = string;
            button.className = "button";
            button.style.padding = ".25rem .5rem";
            button.addEventListener("click", e => {
                e.preventDefault();
                textarea.value += textarea.value.trim() ? `\n${string}` : string;
                document.querySelector('#send_message_form').submit();
            });
            responsesDiv.appendChild(button);
        });

var messageLink = document.evaluate('//*[@id="list_messages_my_conversation_messages_items"]/div/div[1]/a', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);

        if (messageLink && messageLink.singleNodeValue) {
            var messageUrl = messageLink.singleNodeValue.href;

            const parentElement = document.evaluate('//*[@id="list_messages_my_conversation_messages_items"]/div/div[3]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
            const img = document.createElement('img');
            img.src = 'https://samherbert.net/svg-loaders/svg-loaders/three-dots.svg'; // loading spinner
            img.style.height = "32px";
            img.style.width = "32px";
            parentElement.appendChild(img);
            fetchAndExtract(messageUrl, function(htmlDoc) {
                var closeButtons = addCloseButtonsListeners(confirmButton, rejectButton);
                var confirmClose = closeButtons.confirmClose;
                var denyClose = closeButtons.denyClose;
                updateMessageDOM(htmlDoc, confirmClose, denyClose);

            });
        } else {
            console.log('Message link not found.');
        }


        // AESTHETIC CHANGE
        var bottomElement = document.evaluate('//*[@id="list_messages_my_conversation_messages_items"]/div/div[3]/form/div', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        if (bottomElement && bottomElement.singleNodeValue) {
            bottomElement.classList.add('bottom-element');
            bottomElement.singleNodeValue.style.marginTop = "1em";
            bottomElement.singleNodeValue.style.marginBottom = "1em";
            bottomElement.singleNodeValue.style.outline = "2px solid red";
            bottomElement.singleNodeValue.classList.remove("bottom");
        }


    }

    if (isNotPage("https://www.camwhores.tv/my/videos/", "https://www.camwhores.tv/my/")) {


      function addPrivateVideos() {
            const private_videos = [];

            const videoElements = document.querySelectorAll('.line-premium, .line-private');

            videoElements.forEach(video => {
                const parentDiv = video.parentElement;
                if (
                    parentDiv.tagName === 'DIV' &&
                    parentDiv.parentElement.tagName === 'A' &&
                    !processedVideos.has(parentDiv.parentElement)
                ) {
                    private_videos.push(parentDiv.parentElement);
                    processedVideos.add(parentDiv.parentElement);
                }
            });

            return private_videos;
        }

async function checkVideoPage(url) {
    try {
        const response = await fetch(url);
        if (!response.ok) {
            console.error('Error loading video page:', response.status);
            return { byFriend: false, usernameID: null };
        }

        const html = await response.text();
        const parser = new DOMParser();
        const doc = parser.parseFromString(html, 'text/html');
        const hasElement1 = doc.querySelector('body > div.container > div.content > div.block-video > div.video-holder > div.player > div > div > span') !== null;
        const hasElement2 = doc.querySelector('body > div.container > div.content > div.block-album > div.album-holder > div.images > span.message') !== null;
        let username = doc.querySelector("#tab_video_info > div > div.block-user > div.username > a") || doc.querySelector("#tab_album_info > div > div.block-user > div > a")?.textContent.trim();

        var vid_code = doc.querySelector('.username a').href.replace("https://www.camwhores.tv/members/", "");
        var usernameID = vid_code.split("/")[0];

        return { byFriend: hasElement1 || hasElement2, usernameID: usernameID, username: username };
    } catch (error) {
        console.error('Error checking video page:', error);
        return { byFriend: false, usernameID: null };
    }
}

        function updateImageSrc(image) {
            const src = image?.src;
            if (src?.endsWith('1.jpg')) image.src = src.replace('1.jpg', '5.jpg');
        }

        async function applyStyleToPrivateVideos() {
            for (const video of addPrivateVideos()) {
                const img = video.querySelector('div > img');
                const link = video.href;
                if(!link) continue;
                const { byFriend, usernameID, username } = await checkVideoPage(link);

                if (!byFriend) {
                    video.style.outline = 'green solid 2px';
                    let linePrivate = video.querySelector('.line-private');
                    if (linePrivate) linePrivate.style.display = 'none';

                    if (img) img.style.opacity = '1';

                } else {
                    const linePrivate = video.querySelector('.line-private > span');
                    if (linePrivate) {
                        linePrivate.textContent += ' ❌';
                        const addFriend = document.createElement('a');
                        Object.assign(addFriend, {
                            textContent: '➕',
                            title: 'Friend request',
                            href: `https://www.camwhores.tv/members/${usernameID}/#friends`,
                            style: 'z-index: 10; position: absolute; bottom: 20px; right: 0; font-size: 1rem; padding: 5px 2.5px; background-color: hsla(0, 0%, 0%, 0.7);'
                        });
                        addFriend.addEventListener('mousedown', e => {
                            e.stopImmediatePropagation();
                            friendRequestSetup(video.querySelector('strong.title').textContent.trim(), link, usernameID);
                            // console.log("Added video to data")
                        });

                        video.querySelector('.img').appendChild(addFriend);
                    }
                }
                if (alternative_thumbnails_users.includes(username)) updateImageSrc(img);
                await new Promise(resolve => setTimeout(resolve, 25)); // avoid 503
            }
        }

        function highlightKeywords() {
            document.querySelectorAll('.title').forEach(title => {
                highlight_keywords.forEach(keyword => {
                    title.innerHTML = title.innerHTML.replace(new RegExp(keyword, 'gi'), '<span style="color: green;">$&</span>');
                });
            });
        }

        applyStyleToPrivateVideos();
        highlightKeywords();
        setInterval(() => {
            applyStyleToPrivateVideos();
            highlightKeywords();
        }, 500);

    }

    if (window.location.href.startsWith("https://www.camwhores.tv/members/")) {
        const userId = window.location.href.split('/').slice(-2, -1)[0];
        const userVideosH2 = document.querySelector('#list_videos_uploaded_videos .headline h2');
        const userDescription = document.querySelector("div.block-profile > div.profile-list > div > div.about-me > div > em");
        const lastLogin = document.querySelector('div.block-profile > div.profile-list > div.margin-fix > div.last-login > div.item')?.textContent.trim();
        const h1 = document.querySelector("h1");
        const country = document.querySelector('div.block-profile > div.profile-list > div.margin-fix > div.column > div:nth-child(1) > em');
        const countryName = country.textContent.trim();
        const countryCode = contryCodesArray()[countryName] || [];
        const parent = document.querySelector('div.block-profile > div.profile-list > div.margin-fix > div.column > div:nth-child(1)');
        const recentlyOnline = convertToSeconds(lastLogin) <= 300;

        function userProfileAsyncURL(uid, page) {
            return `https://www.camwhores.tv/members/${uid}/?mode=async&function=get_block&block_id=list_videos_uploaded_videos&sort_by=&from_videos=${page}&_=${Date.now()}`;
        }

        function numberBetweenParenthesis(text) {
            return text?.match(/\((\d+)\)/)?.[1] ?? null;
        }

        function getRoundedTotalPages(h2) {
            const total = numberBetweenParenthesis(h2?.textContent);
            return total ? Math.ceil(total / 5) : null;
        }

        function updateVideoH2() {
            const roundedPages = getRoundedTotalPages(userVideosH2);
            if (roundedPages) {
                userVideosH2.innerHTML = userVideosH2.innerHTML.includes("Page") ? userVideosH2.innerHTML : userVideosH2.innerHTML.trim() + `/${roundedPages}`;
            } else {
                console.log("No rounded pages.");
            }
        }

        function addSearchButton() {
            if(document.querySelector('#cum_vid_search')) return;
            let sortElement = document.querySelector("#list_videos_uploaded_videos > div.headline > div.sort");
            if (sortElement) {
                const searchButton = document.createElement('div');
                searchButton.className = 'user-search';
                searchButton.innerHTML = '<strong>Search videos</strong><span class="icon type-search"></span>';
                searchButton.id = 'cum_vid_search';
                searchButton.addEventListener('click', searchDialog);
                sortElement.parentNode.insertBefore(searchButton, sortElement);
            }
        }


        function searchDialog() {
                if (document.querySelector('#search-dialog')) return;


                console.log("Creating dialog...");
            let dialog = document.createElement('div');
            dialog.id = "search-dialog";
            dialog.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%, -50%);backdrop-filter:blur(5px);background:rgba(0,0,0,0.44);padding:20px;border-radius:10px;box-shadow:0 4px 8px rgba(0,0,0,0.1);z-index:1000;';
            let searchHighlights = retrieveValueFromStorage("highlights_search") || [];
            dialog.innerHTML = `
            <form style="padding-top:10px" id="cum_form_query">
                <div class="fancybox-item fancybox-close" style="position:absolute;top:5px;right:5px;"></div>
                <input type="text" autofocus placeholder="Search..." style="margin-right:5px;padding:5px;">
                <button type="submit" style="padding:5px;box-sizing:border-box;height:30px;cursor:pointer;">Search</button>
            </form>
            <hr style="margin-top:10px">
            <form style="padding-top:10px" id="cum_form_checkbox">
            ${highlight_keywords.map((keyword, index) => `
                <label>
                    <input type="checkbox" name="${keyword}" style="padding:5px;" ${searchHighlights.includes(keyword) ? 'checked' : ''}>
                    ${keyword}
                </label>
            `).join('')}

                <button id="cum_search_checkbox" type="button" style="display:block;padding:5px;box-sizing:border-box;height:30px;cursor:pointer;width:100%;margin:10px 0;">Search selected</button>
            </form>
                <button id="cum_search_all" type="button" style="display:block;padding:5px;box-sizing:border-box;height:30px;cursor:pointer;width:100%;margin-top:5px;" title="Search, ${highlight_keywords.join(", ")}">Search all highlights</button>
            `;

    document.body.insertBefore(dialog, document.body.firstChild);
    console.log("Dialog added to body.");

              dialog.querySelector('#cum_search_checkbox').addEventListener('click', e => {
                  e.preventDefault();
                  const selectedKeywords = Array.from(dialog.querySelectorAll('input[type="checkbox"]:checked'))
                      .map(checkbox => checkbox.name);
                  saveValue("highlights_search", selectedKeywords)
                  console.log(selectedKeywords)
                  displayResults(selectedKeywords);
                  document.body.removeChild(dialog);
              });




            dialog.querySelector('#cum_form_query').addEventListener('submit', e => {
                e.preventDefault();
                const input = dialog.querySelector('input').value;
                if (input) displayResults(input);
                document.body.removeChild(dialog);
            });
            dialog.querySelector('#cum_search_all').addEventListener('click', e => {
                e.preventDefault();
                displayResults("");
                document.body.removeChild(dialog);
            });
            dialog.querySelector('.fancybox-close').addEventListener('click', e => {
                e.preventDefault();
                document.body.removeChild(dialog);
            });
        }


        async function displayResults(query) {
            try {
                const resultList = document.createElement('div');
                resultList.id = 'cum_search_results';
                resultList.className = "list-videos";
                const loadingContainer = document.createElement('div');
                loadingContainer.style.textAlign = 'center';
                loadingContainer.innerHTML = '<img src="https://samherbert.net/svg-loaders/svg-loaders/three-dots.svg" alt="Loading..." style="width:50px;height:50px;">';
                document.querySelector("#list_videos_uploaded_videos").appendChild(loadingContainer);

                const results = await videoSearch(userVideosH2, userId, query);
                loadingContainer.remove();
                resultList.innerHTML = `<div class="headline" style="padding:15px 5px 7px 5px;"><h2>Search results</h2></div>`;
                if (!results.length) {
                    resultList.textContent = "No results found";
                } else {
                    resultList.innerHTML += results.map(video => `
                    <div class="item" style="margin-left:11px;">
                        <a href="${video.url}" title="${video.title}">
                            <div class="img"><img class="thumb lazy-load" src="${video.imgSrc}" alt="${video.title}" width="180" height="135"></div>
                            <strong class="title">${video.title}</strong>
                            <div class="wrap"><div class="duration">${video.duration}</div></div>
                        </a>
                    </div>`).join('');
                }

                const existingResult = document.querySelector("#cum_search_results");
                if (existingResult) existingResult.replaceWith(resultList);
                else document.querySelector("#list_videos_uploaded_videos").insertAdjacentElement('afterend', resultList);
            } catch (error) {
                console.error("Error:", error);
            }
        }



async function videoSearch(userVideosH2, uid, query) {
    let searchTerms = null;
    let totalVideoPages = getRoundedTotalPages(userVideosH2);
    let approxTotalVideos = totalVideoPages * 5;
    let timeout;
    if (approxTotalVideos > 1000) {
        timeout = approxTotalVideos / 2;
    }else if (approxTotalVideos > 500) {
        timeout = approxTotalVideos / 5;
    } else {
        timeout = 75;
    }
    if (Array.isArray(query)) {
        searchTerms = query.map(q => q.toLowerCase());
    } else {
        searchTerms = query ? [query.toLowerCase()] : highlight_keywords.map(k => k.toLowerCase());
    }
    if (totalVideoPages !== null) {
        let resultArray = [];
        let fetchPromises = [];

        for (let pageInt = 1; pageInt <= totalVideoPages; pageInt++) {
            let nextFivePages = userProfileAsyncURL(uid, pageInt);
            let fetchPromise = new Promise((resolve) => {
                setTimeout(() => {
                    fetchAndParseHTML(nextFivePages, (htmlDoc) => {
                        let videos = htmlDoc.querySelectorAll('div#list_videos_uploaded_videos_items div.item');
                        videos.forEach(video => {
                            let title = video.querySelector('a').getAttribute('title');
                            let url = video.querySelector('a').getAttribute('href');
                            let imgSrc = video.querySelector('img').getAttribute('data-original');
                            let duration = video.querySelector('.duration').textContent.trim();
                            let isPrivate = video.classList.contains('private');
                            let lowerTitle = title.toLowerCase();
                            if (searchTerms.some(term => lowerTitle.includes(term))) {
                                resultArray.push({ title, private: isPrivate, url, imgSrc, duration });
                            }
                        });
                        resolve();
                    });
                }, timeout * (pageInt - 1));
            });
            fetchPromises.push(fetchPromise);
        }

        await Promise.all(fetchPromises);
        return resultArray;
    } else {
        console.log("ERROR in totalVideoPages:", totalVideoPages);
        return null;
    }
}


        function getCurrentUserVideoLocation() {
            return sessionStorage.getItem(`${currentUserID()}:https://www.camwhores.tv/members/${userId}/#list_videos_uploaded_videos:params`);
        }

        function waitForSessionStorageChange() {
            const initialValue = getCurrentUserVideoLocation();
            const interval = setInterval(() => {
                if (getCurrentUserVideoLocation() !== initialValue) {
                    clearInterval(interval);
                    updateVideoH2();
                    addSearchButton();
                }
            }, 50);
        }

        if (recentlyOnline) {
            h1.textContent += " (online)";
            h1.className = "online";
        }
        userDescription.textContent = userDescription.textContent.trim();

        if (countryCode && countryName !== "no info") {
            country.remove();
            const img = document.createElement('img');
            img.src = `https://flagcdn.com/${countryCode}.svg`;
            img.width = 35;
            img.alt = countryName;
            img.title = countryName;
            img.style.cssText = "position:absolute;top:50%;transform:translateY(-50%);left:4rem;";
            parent.appendChild(img);
            parent.style.position = "relative";
        }

        updateVideoH2();
        // addButtonEventListener();
      new MutationObserver(() => addSearchButton())
  .observe(document.body, { childList: true, subtree: true });


    if (window.location.href.endsWith("#friends") && !window.location.href.includes("add_to_friends_done")) {
          var userVideoData,lastCumTitle,lastVidCode,lastUserID;
          let addButton = document.querySelector('a[href="#friends"][data-action="add_to_friends"]');
          let textarea = document.getElementById('add_to_friends_message');
          let userId = window.location.href.split('/').slice(-2, -1)[0];
          let confirmButton = document.querySelector('input[name="confirm"]');


          let retrievedData = JSON.parse(localStorage.getItem('last_cum_video_data')) || {};
          console.log("uid:",userId,retrievedData[userId])
          if (retrievedData[userId]) {
              userVideoData = retrievedData[userId];
              console.log(userVideoData);
              lastCumTitle = userVideoData.title;
              lastVidCode = userVideoData.videoId;
              lastUserID = userVideoData.userId;
          } else {
              console.log("No data found for this user.");
          }


          let pageTitle = document.title;
          let username = pageTitle.replace("'s Page", "");
          if (textarea !== null
              && !addButton.classList.contains('done')
              // && username !== lastAutoFriend
              && lastCumTitle !== null
              && addButton !== null) {
              textarea.value = "";
              textarea.value += friend_request_text + " " + lastCumTitle + "\n##" + lastVidCode;

              // addButton.click();
              document.querySelector('input[type="submit"].submit[value="Invite"]').click();  // final submit button
          }


          // console.log(confirmButton)
          // if(confirmButton) confirmButton.click();


          if (addButton.classList.contains('done') && history.length === 1) {
              window.close();
          }
        // console.log("Should add?")
      }


      else if (window.location.href.includes("add_to_friends_done")) {
          let addButton = document.querySelector('a[href="#friends"][data-action="add_to_friends"]');
          if (addButton && !addButton.classList.contains('done')) {
              addButton.classList.add('done');
          }
          if (history.length === 1 || !document.hasFocus()) window.close();
      }

    }

    if (window.location.href.startsWith("https://www.camwhores.tv/videos/")) {
        const el = document.querySelector('body > div.container > div.content > div.block-video > div.video-holder > div.player > div > div > span') ||
            document.querySelector('body > div.container > div.content > div.block-album > div.album-holder > div.images > span.message');
        const href = document.querySelector('#tab_video_info > div > div.block-user > div > a').href;
        const userId = document.querySelector('.username a')?.getAttribute('href').split('/').slice(-2, -1)[0];

        if (el) {
            fetch(href)
                .then(response => response.text())
                .then(html => {
                    const doc = new DOMParser().parseFromString(html, 'text/html');
                    const infoMsg = doc.querySelector('.info-message');
                    const headline = document.querySelector('body > div.container > div.content > div.headline > h1');
                    const isFriends = infoMsg?.textContent.includes("is in your friends list.");
                    const isPending = window.location.href.includes("please_check_friend");

                    if (isFriends) {
                        el.textContent = "You're already friends! Reloading in 4 seconds";
                        setTimeout(() => location.reload(), 4000);
                    } else if (isPending) {
                        el.innerHTML += "<br><br>Friendship confirmed? Reloading in 4 seconds";
                        setTimeout(() => location.reload(), 4000);
                    } else if (headline) {
                        friendRequestSetup(headline.textContent.trim(), window.location.href, userId);
                        el.innerHTML += `<br><a class="button" href="${el.querySelector('a').href}#friends" style="display:inline-block;">Send request</a>`;
                    }

                    document.querySelectorAll('.block-screenshots .item.private').forEach(el => {
                        const img = el.querySelector('img');
                        if (img) {
                            el.replaceWith(Object.assign(document.createElement('a'), {
                                innerHTML: el.innerHTML,
                                href: img.dataset.original.replace('/videos_screenshots/', '/videos_sources/').replace('/180x135/', '/screenshots/'),
                                className: 'item',
                                rel: 'screenshots',
                                'data-fancybox-type': 'image'
                            }));
                        }
                    });
                })
                .catch(console.error);
        }

        function waitForElementExists(parent, selector) {
            return new Promise(resolve => {
                new MutationObserver((_, observer) => {
                    const el = parent.querySelector(selector);
                    if (el) {
                        resolve(el);
                        observer.disconnect();
                    }
                }).observe(parent, { childList: true, subtree: true });
            });
        }

        function addDownloadLink(video) {
            if (document.getElementById('downloadVideoLink')) return;
            const url = video.getAttribute('src');
            const name = `${document.querySelector('.headline').innerText}.mp4`;
            document.querySelector('div.video-holder > div.video-info > div > div.info-buttons > div.tabs-menu > ul')
                .insertAdjacentHTML('afterbegin', `<li><a id="downloadVideoLink" class="toggle-button" href="${url}" download="${name}">Download</a></li>`);
        }

        waitForElementExists(document.body, 'video').then(addDownloadLink).catch(console.error);

function addTimestampsToVideo(timestamps){
      var tabScreenshots = document.getElementById('tab_screenshots');
      var timestampsDiv = document.getElementById('timestamps');
      if (timestampsDiv) return;
                          // Check if #tab_screenshots exists

      // Check if #timestamps exists inside #tab_screenshots
          timestampsDiv = document.createElement('div');
          timestampsDiv.id = 'timestamps';
          timestampsDiv.style = 'display:flex;gap:.1rem';
          tabScreenshots.appendChild(timestampsDiv);


      timestamps.forEach(function (timestamp, index) {
          var formattedTimestamp = formatTimeFromSeconds(timestamp);

          // Create button for timestampsDiv
          var button1 = document.createElement('button');
          button1.textContent = formattedTimestamp;
          button1.addEventListener('click', function () {
              flowplayer().seek(timestamp);
          });
          timestampsDiv.appendChild(button1);

          // Create button for screenshot link
          var screenshotLinks = document.querySelectorAll('.block-screenshots a[rel="screenshots"]');
          if (screenshotLinks[index]) {
              var button2 = document.createElement('button');
              button2.textContent = formattedTimestamp;
              button2.style.position = 'absolute';
              button2.addEventListener('click', function () {
                  flowplayer().seek(timestamp);
              });
              screenshotLinks[index].style.position = 'relative';
              screenshotLinks[index].appendChild(button2);
          }
      });



}
            function waitForVideoDuration(maxAttempts, intervalMs) {
                var attempts = 0;

                // Approximated percentages
                var percentages = [/* 0:14, */ 25, 40, 59.75, 79]; // CONSIDER 0:14 IN FRONT IF EVERYTHING! (15:49)

                function checkDuration() {
                    var player = flowplayer();
                  // console.log(player, typeof player.video.duration === 'number')
                    if (player && player.video && typeof player.video.duration === 'number') {
                        var videoDuration = player.video.duration;

                        // Calculate percentages based on approximations
                        var calculatedPercentages = percentages.map(function (percent) {
                            return (percent / 100) * videoDuration;
                        });

                        console.log("Video Duration:", videoDuration);
                        console.log("Calculated Percentages:", calculatedPercentages);

                        // Determine the first timestamp as the minimum between 14 seconds and 5% of video duration
                        var firstTimestamp = 14;



                        // Create buttons with timestamps
                        var timestamps = [firstTimestamp].concat(calculatedPercentages); // Include the first timestamp
                        addTimestampsToVideo(timestamps);

                    } else {
                        attempts++;
                        setTimeout(checkDuration, intervalMs);
                    }
                }

                checkDuration();
            }

            // Helper function to format time from seconds to mm:ss
            function formatTimeFromSeconds(seconds) {
                var date = new Date(null);
                date.setSeconds(seconds);
                return date.toISOString().substr(14, 5);
            }

            waitForVideoDuration(10, 500);

            document.addEventListener('visibilitychange', function() {
                if (document.getElementById('tab_screenshots')) return;
                if (document.visibilityState === 'visible') {
                  console.log("retrying timestamps")
                   waitForVideoDuration(10, 500);
                }
            });



    }

    if (window.location.href.startsWith("https://www.camwhores.tv/playlists/") && window.location.href !== "https://www.camwhores.tv/playlists/") {
        const h1Element = document.querySelector('h1');
        const playlistItem = document.querySelector('#playlist_view_playlist_view_items a');
        h1Element.textContent = playlistItem.querySelector('.title').textContent.trim();

        h1Element.parentElement.insertAdjacentHTML('beforeend', `<a class="button" style="padding: 5px 5px 0 5px;" href="${playlistItem.href}">Open video</a>`);

        document.addEventListener('click', e => {
            const clickedItem = e.target.closest('#playlist_view_playlist_view_items a');
            if (clickedItem) {
                h1Element.textContent = clickedItem.querySelector('.title').textContent.trim();
                document.querySelector('.button').href = clickedItem.href;
            }
        });
    }

    if (window.location.href.startsWith("https://www.camwhores.tv/edit-video/")) {
        document.querySelector('.section-title.expand[data-expand-id="tab_screenshots"]').click();
        document.querySelectorAll("#tab_screenshots > div > div:nth-child(-n+5) > div.item-control > div.item-control-holder").forEach(el =>
            el.addEventListener('click', () => {
                document.querySelector('input.submit[value="Save"]').click();
                if (history.length === 1) window.close();
            })
        );
    }

    if (window.location.href === "https://www.camwhores.tv/my/") {
        const profileCSS = document.createElement('style');
        profileCSS.className = "profile-css";
        profileCSS.textContent = `
        #edit_profile_about_me{height:225px;}
        strong.popup-title,span.selection [role="combobox"]{display:none;}
        body > div.fancybox-overlay.fancybox-overlay-fixed > div{top:0 !important;height:100svh !important;}
        body > div.fancybox-overlay.fancybox-overlay-fixed,body > div.fancybox-overlay.fancybox-overlay-fixed > *{box-sizing:border-box;}
        div.fancybox-inner{height:100% !important;width:max-content !important;}
        .fancybox-close{top:9px !important;}
    `;
        document.head.appendChild(profileCSS);
    }

    if (window.location.href === "https://www.camwhores.tv/" || window.location.href === "https://www.camwhores.tv") {
        const listVideos = document.querySelector("body > div.container > div.list_videos > div");
        const headline = document.querySelector("body > div.container > div.content > div.main-content > div.headline");
        const latestVideoUpSeconds = convertToSeconds(document.querySelector("#list_videos_featured_videos_items div.added").innerText);
        const latestStoredUpSeconds = parseInt(localStorage.getItem("cum_last_featuredVideoUpDate"));
        const showFeaturedVids = retrieveValueFromStorage("options")["cum_show_top"];
        const thereIsANewerVideo = latestVideoUpSeconds < latestStoredUpSeconds;

        const hideFtVideos = show => {
            listVideos.style.display = show ? 'block' : 'none';
            headline.style.paddingTop = show ? '20px' : '10px';
            if (!show) localStorage.setItem("cum_last_featuredVideoUpDate", latestVideoUpSeconds);
            document.querySelector("#🆕")?.style.setProperty("display", "none");
        };

        hideFtVideos(showFeaturedVids);

        document.querySelector("body > div.container > div.list_videos").insertAdjacentHTML('afterbegin', `
      <label class="switch" title="Top videos" id="cum_switch_top">
        <input type="checkbox" id="cum_show_top" ${showFeaturedVids ? 'checked' : ''}>
        <span class="slider round"></span>
      </label>
    `);

        document.querySelector('#cum_show_top').addEventListener('change', function () {
            hideFtVideos(this.checked);
            saveArraySettings("options", "cum_show_top", this.checked);
        });

        if (!showFeaturedVids && thereIsANewerVideo) {
            document.querySelector("body > div.container > div.list_videos").insertAdjacentHTML('beforeend', `
            <span id="🆕" style="font-size:1.5rem;vertical-align:middle;"> 🆕</span>
        `);
        }
    }


function extractVideoId(url) {
    if(!url) return console.error("url:", url)
    const parts = url.split('/');
    return parts[4] + '/' + parts[5].replace(/\/$/, '');
}


function extractLatestVideoId(dom = document){
    let latestVid = dom.querySelector("#list_videos_videos_list_search_result_items > div > a");
    if (!latestVid) return null;
    return extractVideoId(latestVid.href)
}

function currentSearchTerm() {
    let string = window.location.pathname.split('/search/')[1]?.split('/')[0].trim() ||
                 h1.textContent.split(':')[1].trim() || null;
    if (!string) return null;
    return string.replace(/%20|-/g, ' ').replace(/\s+/g, ' ').toLowerCase();
}


function updateLatestVideo(dom = document, searchTerm = null){
    let h1 = dom.querySelector('h1');
    if (!h1) return;

    if (!searchTerm) searchTerm = currentSearchTerm();
    if (!searchTerm) return;

    let data = {
        timestamp: Date.now(),
        id: extractLatestVideoId()
    };
    saveArraySettings("auto_search", searchTerm, data);
}

  if (window.location.pathname.startsWith('/search/')) {

        let searchTerm = currentSearchTerm();
        console.log(searchTerm)

        let hypotheticalPresence = retrieveValueFromStorage("auto_search")[searchTerm];
        if(hypotheticalPresence) updateLatestVideo()


        let buttonAutoSearch = document.createElement('button');
        buttonAutoSearch.id = 'cum-auto-search';
        buttonAutoSearch.textContent = 'Add to Wish List';
        buttonAutoSearch.classList.add('btn');
        buttonAutoSearch.style = "transform:translate(5px, 3px);cursor:pointer"
        buttonAutoSearch.addEventListener('click', () => {
            updateLatestVideo();

            buttonAutoSearch.textContent = 'Done!';
            buttonAutoSearch.disabled = true;


        });

        document.querySelector('h1')?.insertAdjacentElement('afterend', buttonAutoSearch);

}









    function myVideosAsyncURL(pageInt) {
        return `https://www.camwhores.tv/my/videos/?mode=async&function=get_block&block_id=list_videos_my_uploaded_videos&sort_by=&from_my_videos=${pageInt}&_=${Date.now()}`;
    }

    function pagesLastProcessedVideo(page, callback) {
        fetchAndParseHTML(myVideosAsyncURL(page), doc => {
            const items = doc.querySelectorAll("#list_videos_my_uploaded_videos_items > form > div.item.private");
            const idx = Array.from(items).reverse().findIndex(item => item.classList.contains('processing'));

            const checkbox = items[items.length - 1 - idx]?.querySelector(".item-control-holder .toggle-button input[type='checkbox']");
            callback(checkbox ? checkbox.value : false);
        });
    }

    async function lastProcessedVideoID(callback) {
        let found = false;
        const promises = Array.from({ length: 3 }, (_, page) =>
            new Promise(resolve => pagesLastProcessedVideo(page + 1, value => {
                if (value) found = true;
                resolve(value);
            }))
        );

        const results = await Promise.all(promises);
        callback(results.find(value => value) || false);
    }

    function loadLastVideoData() {
        return JSON.parse(localStorage.getItem('lastVideoData') || '[]') || [null, null];
    }

    function lastVideoDataAge(refreshMinutes = 30) {
        const [, timestamp] = loadLastVideoData();
        return timestamp ? (Date.now() - timestamp) / 1000 : refreshMinutes * 60 + 1;
    }

    function saveLastVideoID(callback) {
        lastProcessedVideoID(currentID => {
            localStorage.setItem('lastVideoData', JSON.stringify([currentID, Date.now()]));
            callback(loadLastVideoData()[0], currentID);
            console.log(`Last ID: ${loadLastVideoData()[0]}\nCurrent ID: ${currentID} (https://www.camwhores.tv/edit-video/${currentID}/)`);
        });
    }

    function alertNewVideo(newID) {
        if (confirm("A new video of yours has been published.\nGo to My Videos?")) {
            window.location.href = "/my/videos/";
        }
    }

    function updateLastVideoData(minutes = backgroundChecksMinutes) {
        const waitTime = minutes * 60;
        const ageOfCheck = lastVideoDataAge(backgroundChecksMinutes);
        if (ageOfCheck > waitTime) {
            saveLastVideoID((lastID, newID) => {
                if (newID && lastID && lastID !== newID) {
                    alertNewVideo(newID);
                } else {
                    console.log("No new videos were published");
                }
            });
        } else {
            // Optionally log how much time is left before the next check
            // console.log(`Only ${Math.round(ageOfCheck / 60)} minutes have passed, ${minutes - Math.round(ageOfCheck / 60)} to go.`);
        }
    }




    notifyProcessedVideos && updateLastVideoData(backgroundChecksMinutes);


    function contryCodesArray() { return { "Andorra": "ad", "United Arab Emirates": "ae", "Afghanistan": "af", "Antigua and Barbuda": "ag", "Anguilla": "ai", "Albania": "al", "Armenia": "am", "Angola": "ao", "Antarctica": "aq", "Argentina": "ar", "American Samoa": "as", "Austria": "at", "Australia": "au", "Aruba": "aw", "Åland Islands": "ax", "Azerbaijan": "az", "Bosnia and Herzegovina": "ba", "Barbados": "bb", "Bangladesh": "bd", "Belgium": "be", "Burkina Faso": "bf", "Bulgaria": "bg", "Bahrain": "bh", "Burundi": "bi", "Benin": "bj", "Saint Barthélemy": "bl", "Bermuda": "bm", "Brunei": "bn", "Bolivia": "bo", "Caribbean Netherlands": "bq", "Brazil": "br", "Bahamas": "bs", "Bhutan": "bt", "Bouvet Island": "bv", "Botswana": "bw", "Belarus": "by", "Belize": "bz", "Canada": "ca", "Cocos (Keeling) Islands": "cc", "DR Congo": "cd", "Central African Republic": "cf", "Republic of the Congo": "cg", "Switzerland": "ch", "Côte d'Ivoire (Ivory Coast)": "ci", "Cook Islands": "ck", "Chile": "cl", "Cameroon": "cm", "China": "cn", "Colombia": "co", "Costa Rica": "cr", "Cuba": "cu", "Cape Verde": "cv", "Curaçao": "cw", "Christmas Island": "cx", "Cyprus": "cy", "Czechia": "cz", "Germany": "de", "Djibouti": "dj", "Denmark": "dk", "Dominica": "dm", "Dominican Republic": "do", "Algeria": "dz", "Ecuador": "ec", "Estonia": "ee", "Egypt": "eg", "Western Sahara": "eh", "Eritrea": "er", "Spain": "es", "Ethiopia": "et", "European Union": "eu", "Finland": "fi", "Fiji": "fj", "Falkland Islands": "fk", "Micronesia": "fm", "Faroe Islands": "fo", "France": "fr", "Gabon": "ga", "United Kingdom": "gb", "England": "gb-eng", "Northern Ireland": "gb-nir", "Scotland": "gb-sct", "Wales": "gb-wls", "Grenada": "gd", "Georgia": "ge", "French Guiana": "gf", "Guernsey": "gg", "Ghana": "gh", "Gibraltar": "gi", "Greenland": "gl", "Gambia": "gm", "Guinea": "gn", "Guadeloupe": "gp", "Equatorial Guinea": "gq", "Greece": "gr", "South Georgia": "gs", "Guatemala": "gt", "Guam": "gu", "Guinea-Bissau": "gw", "Guyana": "gy", "Hong Kong": "hk", "Heard Island and McDonald Islands": "hm", "Honduras": "hn", "Croatia": "hr", "Haiti": "ht", "Hungary": "hu", "Indonesia": "id", "Ireland": "ie", "Israel": "il", "Isle of Man": "im", "India": "in", "British Indian Ocean Territory": "io", "Iraq": "iq", "Iran": "ir", "Iceland": "is", "Italy": "it", "Jersey": "je", "Jamaica": "jm", "Jordan": "jo", "Japan": "jp", "Kenya": "ke", "Kyrgyzstan": "kg", "Cambodia": "kh", "Kiribati": "ki", "Comoros": "km", "Saint Kitts and Nevis": "kn", "North Korea": "kp", "South Korea": "kr", "Kuwait": "kw", "Cayman Islands": "ky", "Kazakhstan": "kz", "Laos": "la", "Lebanon": "lb", "Saint Lucia": "lc", "Liechtenstein": "li", "Sri Lanka": "lk", "Liberia": "lr", "Lesotho": "ls", "Lithuania": "lt", "Luxembourg": "lu", "Latvia": "lv", "Libya": "ly", "Morocco": "ma", "Monaco": "mc", "Moldova": "md", "Montenegro": "me", "Saint Martin": "mf", "Madagascar": "mg", "Marshall Islands": "mh", "North Macedonia": "mk", "Mali": "ml", "Myanmar": "mm", "Mongolia": "mn", "Macau": "mo", "Northern Mariana Islands": "mp", "Martinique": "mq", "Mauritania": "mr", "Montserrat": "ms", "Malta": "mt", "Mauritius": "mu", "Maldives": "mv", "Malawi": "mw", "Mexico": "mx", "Malaysia": "my", "Mozambique": "mz", "Namibia": "na", "New Caledonia": "nc", "Niger": "ne", "Norfolk Island": "nf", "Nigeria": "ng", "Nicaragua": "ni", "Netherlands": "nl", "Norway": "no", "Nepal": "np", "Nauru": "nr", "Niue": "nu", "New Zealand": "nz", "Oman": "om", "Panama": "pa", "Peru": "pe", "French Polynesia": "pf", "Papua New Guinea": "pg", "Philippines": "ph", "Pakistan": "pk", "Poland": "pl", "Saint Pierre and Miquelon": "pm", "Pitcairn Islands": "pn", "Puerto Rico": "pr", "Palestine": "ps", "Portugal": "pt", "Palau": "pw", "Paraguay": "py", "Qatar": "qa", "Réunion": "re", "Romania": "ro", "Serbia": "rs", "Russia": "ru", "Rwanda": "rw", "Saudi Arabia": "sa", "Solomon Islands": "sb", "Seychelles": "sc", "Sudan": "sd", "Sweden": "se", "Singapore": "sg", "Saint Helena, Ascension and Tristan da Cunha": "sh", "Slovenia": "si", "Svalbard and Jan Mayen": "sj", "Slovakia": "sk", "Sierra Leone": "sl", "San Marino": "sm", "Senegal": "sn", "Somalia": "so", "Suriname": "sr", "South Sudan": "ss", "São Tomé and Príncipe": "st", "El Salvador": "sv", "Sint Maarten": "sx", "Syria": "sy", "Eswatini (Swaziland)": "sz", "Turks and Caicos Islands": "tc", "Chad": "td", "French Southern and Antarctic Lands": "tf", "Togo": "tg", "Thailand": "th", "Tajikistan": "tj", "Tokelau": "tk", "Timor-Leste": "tl", "Turkmenistan": "tm", "Tunisia": "tn", "Tonga": "to", "Turkey": "tr", "Trinidad and Tobago": "tt", "Tuvalu": "tv", "Taiwan": "tw", "Tanzania": "tz", "Ukraine": "ua", "Uganda": "ug", "United States Minor Outlying Islands": "um", "United Nations": "un", "United States": "us", "Uruguay": "uy", "Uzbekistan": "uz", "Holy See (Vatican City State)": "va", "Saint Vincent and the Grenadines": "vc", "Venezuela": "ve", "British Virgin Islands": "vg", "United States Virgin Islands": "vi", "Vietnam": "vn", "Vanuatu": "vu", "Wallis and Futuna": "wf", "Samoa": "ws", "Kosovo": "xk", "Yemen": "ye", "Mayotte": "yt", "South Africa": "za", "Zambia": "zm", "Zimbabwe": "zw" } }

    async function updateAlert() {
        const lastCheck = await retrieveValueFromStorage('lastVersionCheckDate');
        const lastVersionCheckDate = lastCheck ? new Date(lastCheck) : currentDate;
        if (currentDate - lastVersionCheckDate > 3 * 24 * 60 * 60 * 1000) {
            await saveValue('lastVersionCheckDate', currentDate.toString());
            const htmlDoc = await fetchAndParseHTML(metaPage);
            const metaContent = htmlDoc.body.textContent;
            const metaVersionMatch = metaContent.match(/@version\s+([^\s]+)/);
            if (metaVersionMatch) {
                const latestVersion = metaVersionMatch[1];
                if (latestVersion !== currentVersion) {
                    if (confirm('A new version of the CUM script is available.\nPlease allow automatic updates or update it manually.\nOpen in new tab?')) {
                        GM_openInTab("https://sleazyfork.org/en/scripts/491272-camwhores-tv-utilities-mod");
                    } else {
                        toggleChange(updateRemindersOptionName, false);
                    }
                }
            }
        }
    }

    updateReminders && updateAlert();



    blurImages &&
        GM_addStyle(`
        img:not(.smileys-bar img),div.logo a{
            filter: blur(20px);
            transition: filter 0s;
        }
        img:not(.smileys-bar img):hover,div.logo:hover a{
            filter: blur(0px);
            transition: filter .3s;
        }
    `);




})();