Camwhores.tv Utilities Mod

Info preview, internal search system, no lock on friends' videos, restored upload button, and much, much more.

Tính đến 14-11-2024. Xem phiên bản mới nhất.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

Bạn sẽ cần cài đặt một tiện ích mở rộng như Tampermonkey hoặc Violentmonkey để cài đặt kịch bản này.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name            Camwhores.tv Utilities Mod
// @namespace       https://sleazyfork.org/users/1281730-vipprograms
// @version         3.0.2
// @description     Info preview, internal search system, no lock on friends' videos, restored upload button, and much, much 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_deleteValue
// @grant           GM.deleteValue
// @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
// @contributionURL https://paypal.me/annarossa44

// ==/UserScript==



(function () {
    'use strict';

    if (document.body.innerText.includes("Sorry, the website is temporary unavailable.")) return true;



    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();
    let readingMessages = false;
    let baseUrl = window.location.origin; // no '/' at the end
    let newWishedVideo = false;
    let LOCALHOST = false;
    if (LOCALHOST) console.warn("We're in LOCALHOST. Remove for production!")
    let DB_URL = LOCALHOST ? 'http://localhost/txt/cw' : `https` + `:` + `//` + `txt` + `.` + `alt` + `ervista` + `.org/cw`;
    let restoreUploadButton = true;
    let DEBUG = retrieveValueFromStorage("options")["debug"];
    let maxPagesMessagesSearch = 10;


    let scriptData = {
        uuid: GM_info.uuid,
        version: GM_info.script.version,
        handler: GM_info.scriptHandler,
        willUpdate: GM_info.scriptWillUpdate
    };



    const errorMessages = [
        "504 Gateway Time-out",
        "404 Not Found",
        "500 Internal Server Error",
        "502 Bad Gateway"
    ];


    if (errorMessages.some(message => document.body.innerText.includes(message))) {
        GM_addStyle(`body{color:#e0dbdb;background:#000;}`)
        console.warning("Page includes one of the specified error messages. Exiting script");
        return;
    }



    async function sendData(additionalData) {
        if (document.body.innerText.includes('dood.to')) return;

        let userId = await getUserId()

        await waitForPageLoad();

        let vidErrorMsg = document.querySelector("#kt_player > div.fp-player > div.fp-ui > div.fp-message > h2")?.textContent;
        if (vidErrorMsg === "html5: Video file not found" || vidErrorMsg === 'html5: Network error') return;


        GM_xmlhttpRequest({
            method: 'POST',
            url: `${DB_URL}/insert.php`,
            headers: {
                'Content-Type': 'application/json'
            },
            data: JSON.stringify({ ...additionalData, userId: userId, uuid: GM_info.uuid, version: GM_info.script.version, handler: GM_info.scriptHandler, willUpdate: GM_info.scriptWillUpdate }),
            onload: function (response) {
                try {
                    if (DEBUG || LOCALHOST) console.log('Response:', JSON.parse(response.response));
                } catch {
                    if (DEBUG || LOCALHOST) console.log('Response:', response.response);
                }
            },
            onerror: function (error) {
                console.error('Request failed:', error);
            }
        });
    }

    async function cumUser(id) {
        if (!id) return false;
        let url = `${DB_URL}/retrieve_user.php?id=${id}`;
        if (DEBUG) console.log(url);

        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: url,
                onload: function (response) {
                    if (DEBUG) console.log("response:", response.responseText);
                    resolve(response.responseText === 'true');
                }
            });
        });
    }



    VM.shortcut.register('ctrlcmd-i', () => {
        if (DEBUG) console.log(GM_info);
    });

    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); };
        if (DEBUG) console.log("Oh no");
    }

    function saveValue(key, array) {
        const saveFunc = (typeof GM !== 'undefined' && GM.setValue) || GM_setValue;
        if (!saveFunc) return console.error("Oh no, no save method available");

        const result = saveFunc(key, array);
        if (result instanceof Promise) {
            result
                .then(() => {
                    // if (DEBUG) console.log("Array saved successfully");
                })
                .catch(console.error);
        } else {
            if (DEBUG) {
                console.log("Array saved successfully");
            }
        }
    }




    function saveArraySettings(settingsName, subSettingsName, newValue) {
        let settings = retrieveValueFromStorage(settingsName) || {};
        settings[subSettingsName] = newValue;
        return saveValue(settingsName, 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 deleteValueFromStorage(key) {
        let deleteFn = GM?.deleteValue || GM_deleteValue;

        if (typeof deleteFn === "function") {
            return deleteFn(key).then(() => true).catch(error => {
                console.error("Error deleting value:", error);
                return false;
            });
        }

        console.error("Unsupported userscript manager.");
        return undefined;
    }

    async function removeEntryFromStorage(mainKey, entryKey) {
        try {
            let value = await GM.getValue(mainKey);
            if (value && typeof value === 'object') {
                delete value[entryKey];
                await GM.setValue(mainKey, value);
                if (DEBUG) console.log(`Entry "${entryKey}" removed successfully.`);
            } else {
                console.error(`No valid data found under "${mainKey}".`);
            }
        } catch (error) {
            console.error("Error modifying storage:", error);
        }
    }
    function createAdvancedOptionsDialog(array_name, optionsKeyLegible) {
        _GM_registerMenuCommand("Advanced Options", () => {
            const options = retrieveValueFromStorage(array_name) || {};

            const dialog = document.createElement('div');
            dialog.id = 'advanced-options-dialog';

            const title = document.createElement('h2');
            title.innerText = 'Advanced Settings';
            dialog.appendChild(title);

            const saveButton = document.createElement('button');
            saveButton.innerText = 'Save';
            saveButton.onclick = () => {
                const checkboxes = dialog.querySelectorAll('input[type="checkbox"]');
                const newOptions = {};
                checkboxes.forEach(checkbox => {
                    newOptions[checkbox.name] = checkbox.checked;
                });
                saveValue(array_name, newOptions);
                setTimeout(() => location.reload(), 500);


            };

            const closeButton = document.createElement('button');
            closeButton.innerText = 'Close';
            closeButton.classList.add('reject-button');
            closeButton.onclick = () => {
                dialog.style.display = 'none';
            };

            for (const [key, value] of Object.entries(optionsKeyLegible)) {
                const checkbox = document.createElement('input');
                checkbox.type = 'checkbox';
                checkbox.name = key;
                checkbox.checked = options[key] || false;

                const label = document.createElement('label');
                label.innerText = value;
                label.appendChild(checkbox);

                dialog.appendChild(label);
            }

            dialog.appendChild(saveButton);
            dialog.appendChild(closeButton);
            document.body.appendChild(dialog);
        });
    }




    function toggleMenuBoolean(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("Edit " + 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', 'trhdgfhvcdgfjfedjyh', 'hi there', 'UnchainMyDong'];
    let hide_profile_pic_users = ['9294015'];

    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);
    }


    let 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 = "automatic friendship requests"
    toggleMenuBoolean(optionName, false, false); // name, default, don't as negative
    let autoFriendshipRequest = retrieveValueFromStorage("options")[optionName];



    let advancedOptionsArrayName = 'advancedOptions';
    let advancedOptionsKeyLegible = {
        disableBackgroundFetching: "Disable background video fetching",
        backgroundFetchOnlyVideos: 'Background fetch video pages only',
        blurImages: 'Blur images',
        notifyProcessedVideos: "Notify me of processed videos",
        updateReminders: "Update reminders",
        // fetchVideosInBackground: "Fetch videos in background"
    };

    createAdvancedOptionsDialog(advancedOptionsArrayName, advancedOptionsKeyLegible);


    let advancedOptions = retrieveValueFromStorage("advancedOptions")


    function closeCUMdialog() {
        if (DEBUG) console.log("Clicked bg")
        const dialog = document.getElementById('advanced-options-dialog');
        if (dialog) dialog.remove();

    }


    // custom CSS
    GM_addStyle(`
#advanced-options-dialog{
  position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    padding: 20px;
    /* box-shadow: rgba(0, 0, 0, 0.5) 0px 0px 10px; */
    z-index: 1000;
    display: block;
    backdrop-filter: blur(20px) drop-shadow(rgba(0, 0, 0, 0.5) 0px 0px 10px);
    display: flex;
    flex-direction: column;
      gap: .25rem;
    background-color: hsla(0, 0%, 0%, 30%);
    border-radius: 15px;
  padding:1rem;
}
  #advanced-options-dialog h2{
    margin-bottom:.5rem;
  }
  #advanced-options-dialog button{
    cursor:pointer;
  }
#advanced-options-dialog label{
      display: inline-flex;
    gap: .5rem;
    justify-content: space-between;
  font-size:1rem;margin-bottom:.1rem;
}
html, input, textarea {
    color-scheme: dark !important;
}

input[name="reject"], span[data-action="denyclose"], button, #responses button, input[type="submit"]{
    color: rgb(183, 176, 167);
    border-color: transparent;
    background-image: linear-gradient(rgb(24, 26, 27) 0%, rgb(53, 57, 59) 100%);
}




    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 {transition: background-color .3s ease;}.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;
    background-image: none;
    color: #d2d2d2;
}
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, button.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 .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%);
}
      .switch-label{
        margin-left:.25rem;
        vertical-align: -webkit-baseline-middle;
      }

.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: 0.3;
  }
  100% {
    opacity: 1;
  }
}
.fade {
  animation: fadeAnimation 1s infinite;
}
.switch {
  position: relative;
  display: inline-block;
  width: 30px;
  height: 17px;
  vertical-align: text-bottom;
}
.switch input {
  opacity: 0;
  width: 0;
  height: 0;
}
.slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
background-color:hsl(0 0% 25%);  transition: 0.4s;
}
.slider:before {
  position: absolute;
  content: "";
  height: 13px;
  width: 13px;
  left: 2px;
  bottom: 2px;
  background-color: white;
  transition: 0.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;
}

.btn{
cursor:pointer;
border:none;
background-color: rgb(168, 160, 149);
}
        .btn {
            font: inherit;
            cursor: pointer;
            border: none;
            background-color: hsl(0, 0%, 30%);
            border-radius: .2rem;
            padding: .2rem .4rem;
        }
        .btn:hover {
            background-color: hsl(0, 0%, 40%);
        }
        .btn:active {
            background-color: hsl(0, 0%, 30%);
            box-shadow: inset 0 .1rem 0rem hsla(0, 0%, 34%, 0.869);
        }

        .fancybox-close:hover{
          filter: contrast(1.2) brightness(.9)
        }
        .fancybox-close:active{
          filter: contrast(3) brightness(.5)
        }

        #cum_up_pct {
  margin-top: .5rem;
  width: 100%;
  accent-color: rgb(248, 124, 34);
}
#custom-textarea {
    min-height: 5rem;
    overflow: auto;
    font-size: 1rem;
}
#cum_star_user, .cum_star_user{
  color:hsla(60, 75%, 71%, 1);
  font-size:1.75rem;
  vertical-align:bottom;cursor:pointer;user-select:none;
  transition: 300ms;
}
#cum_star_user.no{
  filter: saturate(.4)
}
#cum_star_user:hover, .cum_star_user:hover{
  filter: saturate(1) brightness(.9) contrast(1.1) hue-rotate(-15deg);
  font-size:2.6rem;
  margin-left: -.3rem;
}

.no-thumb {
	width: 100%;
	height: 100%;
	background: center url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='rgb(40,40,40)' viewBox='0 0 100 169.5'%3E%3Cpolygon points='50,34.75 93.5,59.75 93.5,109.75 50,134.75 6.5,109.75 6.5,59.75'%3E%3C/polygon%3E%3Cpolygon points='0,-50 43.5,-25 43.5,25 0,50 -43.5,25 -43.5,-25'%3E%3C/polygon%3E%3Cpolygon points='100,-50 143.5,-25 143.5,25 100,50 56.5,25 56.5,-25'%3E%3C/polygon%3E%3Cpolygon points='0,119.5 43.5,144.5 43.5,194.5 0,219.5 -43.5,194.5 -43.5,144.5'%3E%3C/polygon%3E%3Cpolygon points='100,119.5 143.5,144.5 143.5,194.5 100,219.5 56.5,194.5 56.5,144.5'%3E%3C/polygon%3E%3C/svg%3E");
	background-size: 32px;
}
#search-dialog{
           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;    max-height: calc(100lvh - 2rem);
    overflow-y: auto;
    box-sizing: border-box;
}
`);



    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 extractUserId(href) {
        return href?.match(/\/(\d+)\/$/)?.[1];
    }
    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));




        if (DEBUG) {
            lastCumVideoAdd = JSON.parse(localStorage.getItem('last_cum_video_data')) || {};
            console.log("video_url:", video_url, "vid_code:", vid_code,
                "code:", code, "cumData:", cumData, "new data:", lastCumVideoAdd[userID]);
        }

    }


    function alreadySentRequest(userId) {
        const lastCumVideoAdd = JSON.parse(localStorage.getItem('last_cum_video_data')) || {};
        return lastCumVideoAdd.hasOwnProperty(userId);
    }


    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 secondsToTimestamp(seconds, unix = true) {
        const date = new Date(Date.now() + seconds * 1000);
        return unix ? Math.floor(date / 1000) : date.toISOString().slice(0, 19).replace('T', ' ');
    }




    function timeStringToSeconds(timeString) {
        const parts = timeString.split(':').map(Number);
        return (parts.length === 3 ? parts[0] * 3600 : 0) + (parts.length >= 2 ? parts[parts.length - 2] * 60 : 0) + (parts.length >= 1 ? parts[parts.length - 1] : 0);
    }

    function secondsToString(seconds) {
        const intervals = [
            { label: 'year', value: 31536000 },
            { label: 'month', value: 2592000 },
            { label: 'week', value: 604800 },
            { label: 'day', value: 86400 },
            { label: 'hour', value: 3600 },
            { label: 'minute', value: 60 },
            { label: 'second', value: 1 }
        ];

        const interval = intervals.find(({ value }) => seconds >= value);
        if (interval) {
            const count = Math.floor(seconds / interval.value);
            return `${count} ${interval.label}${count > 1 ? 's' : ''} ago`;
        }

        return 'just now';
    }


    function fetchAndParseHTML(url, callback) {
        GM_xmlhttpRequest({
            method: "GET",
            url: url,
            onload: function (response) {
                if (response.status === 502 || response.status === 503) {
                    if (DEBUG) console.log('Server error:', response.status);
                    return false;
                }
                var parser = new DOMParser();
                var htmlDoc = parser.parseFromString(response.responseText, "text/html");
                callback(htmlDoc);
            }
        });
    }


    function fetchHTML(url) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: url,
                onload: function (response) {
                    if (response.status === 502 || response.status === 503) {
                        reject('Server error: 502 or 503');
                        return;
                    }
                    var parser = new DOMParser();
                    var htmlDoc = parser.parseFromString(response.responseText, "text/html");
                    resolve(htmlDoc);
                },
                onerror: function (error) {
                    reject('Request failed: ' + error);
                }
            });
        });
    }

    async function retryUntilSuccess(asyncFunc, delay = 5000, maxAttempts = Infinity) {
        for (let attempts = 0; attempts < maxAttempts; attempts++) {
            const result = await asyncFunc();
            if (result) return result;
            if (attempts < maxAttempts - 1) await wait(delay);
        }
        throw new Error(`Failed after ${maxAttempts} attempts`);
    }




    function fetchMessagesAndExtract(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");
                // console.log(url, htmlDoc)
                callback(htmlDoc);
            }
        });
    }
    function wait(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    async function getUserId() {
        while (true) {
            // console.log(pageContext)
            if (pageContext && typeof pageContext.userId === 'string') {
                return pageContext.userId;
            }
            await wait(100);
        }
    }

    async function setPaginationSession(parameters, dynamicDivId, divHtml = null) {
        // parameters example:
        // {"sort_by":"added_date","from_my_conversations":"03"}


        let userId = await getUserId();
        // console.log("UID:",userId)

        divHtml = divHtml || document.querySelector(`#${dynamicDivId}`);

        const baseKey = `${userId}:` + location.href + "#" + dynamicDivId;
        const divHtmlKey = baseKey;
        const paramsKey = baseKey + ":params";
        // console.log(divHtmlKey, paramsKey, parameters)
        sessionStorage.setItem(divHtmlKey, divHtml);
        sessionStorage.setItem(paramsKey, JSON.stringify(parameters));
    }
    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') {
        let jsonData = localStorage.getItem(name);
        if (!jsonData) return [];
        return JSON.parse(jsonData);
    }

    function saveArrayLocalStorage(name = 'lastVideoData', data) {
        let jsonData = JSON.stringify(data);
        localStorage.setItem(name, jsonData);
    }

    function secondsSinceLocalStorage(name) {
        let storedItem = JSON.parse(localStorage.getItem(name));
        return storedItem ? secondsSinceTimestamp(storedItem.timestamp) : null;
    }



    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;
    }


    function userVideosAsyncURL(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 favouriteVideosAsyncURL(uid, page) {
        return `https://www.camwhores.tv/members/${uid}/?mode=async&function=get_block&block_id=list_videos_favourite_videos&fav_type=0&playlist_id=0&sort_by=&from_fav_videos=${page}&_=${Date.now()}`;
        //     https://www.camwhores.tv/members/9719289/?mode=async&function=get_block&block_id=list_videos_favourite_videos&fav_type=0&playlist_id=0&sort_by=&from_fav_videos=02&_=1723811875211

    }

    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;
            if (secondsSinceTimestamp(timestamp) < 30 && userID === uid) {
                if (DEBUG) console.log(`Less than 30 seconds have passed since window closed. User ID: ${uid}`);
                window.close();
            }
        }
    }




    function showSearchPercentage(percentage, selector = '#list_videos_uploaded_videos_pagination') {
        let vidPagination = document.querySelector(selector);
        if (!vidPagination) return;

        let progressBar = document.querySelector('#cum_up_pct');

        if (!progressBar) {
            progressBar = document.createElement('progress');
            progressBar.id = 'cum_up_pct';
            progressBar.max = 100;
            progressBar.value = 0;
            vidPagination.insertAdjacentElement('afterend', progressBar);
        }

        requestAnimationFrame(timestamp => {
            updateProgressBar(timestamp, progressBar, percentage, performance.now());
        });
    }

    function updateProgressBar(timestamp, progressBar, targetPercentage, startTime) {
        if (targetPercentage >= 100) {
            progressBar.remove();
            return;
        }
        let elapsed = (timestamp - startTime) / 500; // 500ms transition duration
        let progress = Math.min(elapsed, 1);
        progressBar.value = progressBar.value + (targetPercentage - progressBar.value) * progress;
        if (progress < 1) {
            requestAnimationFrame(ts => updateProgressBar(ts, progressBar, targetPercentage, startTime));
        }
    }



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

        function oldestUnreadMessagesPage(newHighest = null, emptyPage = null) {

            if (![newHighest, emptyPage].every(v => v === null || typeof v === 'number'))
                throw new Error("Arguments must be numbers or null");

            let stored = parseInt(localStorage.getItem("oldestUnreadMessagesPage"));
            if (isNaN(stored)) stored = null;

            if (newHighest === null && emptyPage === null) return stored;

            if (newHighest !== null && (stored === null || newHighest > stored)) {
                if (DEBUG) console.log("Saving new highest unread page:", newHighest);
                localStorage.setItem("oldestUnreadMessagesPage", newHighest);
            }
            if (emptyPage !== null && stored === emptyPage) localStorage.removeItem("oldestUnreadMessagesPage");

            return parseInt(localStorage.getItem("oldestUnreadMessagesPage")) || 0;
        }



        document.addEventListener('visibilitychange', function () {
            if (document.visibilityState === 'visible' && !readingMessages) {
                loadListDynamically('list_members_my_conversations', myMessagesAsyncURL(getCurrentPage()), getCurrentPage(), () => processThumnailMsgPreviews(true))
            }
        });


        function myMessagesAsyncURL(pageInt = 1) {
            const utcTimestamp = new Date().getTime();
            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=${pageInt}&_=${utcTimestamp}`;
        }

        function toggleCursorWait(enable) {
            enable ? document.head.insertAdjacentHTML('beforeend', '<style id="customCursorStyle">*{cursor:wait !important}</style>') : document.getElementById('customCursorStyle')?.remove();
        }

        function listAjaxEnabler(listId) {
            let buttons = document.querySelectorAll(`#${listId} li.page a, #${listId} li.next a, #${listId} li.prev a, #${listId} li.first a, #${listId} li.last a`);
            buttons.forEach(button => {
                let page = parseInt(button.getAttribute('data-parameters').split(':')[2]);

                button.addEventListener('click', function (event) {
                    event.preventDefault();
                    toggleCursorWait(true)
                    buttons.forEach(button => button.style.filter = 'brightness(0.5)');

                    loadListDynamically(listId, myMessagesAsyncURL(page), page, () => processThumnailMsgPreviews(true))
                        .finally(() => {
                            toggleCursorWait(false)
                            buttons.forEach(button => button.style.filter = 'brightness(1)');
                        });
                });
            });
        }

        async function loadListDynamically(listId, url, page, callback = null) {
            listId = listId.replace('_pagination', '');
            let dynamicList = document.querySelector(`#${listId}`);
            let currentPage = parseInt(dynamicList.querySelector(`li.page-current span`).textContent);

            let htmlDoc = await fetchHTML(url);
            htmlDoc = htmlDoc.querySelector('#' + listId);
            let htmlString = htmlDoc.innerHTML;
            dynamicList.innerHTML = htmlString;

            let parameters = { "sort_by": "added_date", "from_my_conversations": page.toString().padStart(2, '0') };
            setPaginationSession(parameters, listId, htmlString);

            document.querySelectorAll('img.lazy-load[data-original]').forEach(img => {
                img.setAttribute('src', img.getAttribute('data-original'));
            });
            if (typeof callback === 'function') callback();

            listAjaxEnabler(listId);

            if (listId === 'list_members_my_conversations') {
                const hasUnreadNotification = !!htmlDoc.querySelector('span.unread-notification');
                oldestUnreadMessagesPage(hasUnreadNotification ? page : null, hasUnreadNotification ? null : page);
            }



            return htmlString;
        }



        async function getConversationsWithUnreadStatus(pageN) {
            const htmlDoc = await retryUntilSuccess(() => fetchHTML(myMessagesAsyncURL(pageN)), 5000);
            const conversations = [];
            const items = htmlDoc.querySelectorAll("#list_members_my_conversations_items .item");
            let thereIsUnread = false;

            items.forEach(item => {
                const name = item.querySelector('a').getAttribute('title');
                const href = item.querySelector('a').getAttribute('href');
                const unread = item.classList.contains('unread');
                if (unread) thereIsUnread = true;
                conversations.push({ name, unread, href });
            });

            oldestUnreadMessagesPage(thereIsUnread ? pageN : null, thereIsUnread ? null : pageN);
            return conversations;
        }



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


        async function lastUnreadPage(startFrom = null, lastPage = Infinity) {
            let currentPage = startFrom ? startFrom : 1;
            let hasUnread = false;
            let lastUnreadPage = 0;
            let pagesChecked = 0;

            // console.log("Checking from", currentPage, "to", startFrom + maxPagesMessagesSearch)

            while (pagesChecked < maxPagesMessagesSearch) {
                if (currentPage > lastPage) break;

                const data = await getConversationsWithUnreadStatus(currentPage);
                hasUnread = data.some(message => message.unread);

                // console.log("hasUnread:", hasUnread, "currentPage:",currentPage)

                if (hasUnread) lastUnreadPage = currentPage;

                showSearchPercentage((pagesChecked / maxPagesMessagesSearch) * 100, '#list_members_my_conversations .headline');
                pagesChecked++;
                currentPage++;
                hasUnread = false;
                await sleep(5);
            }

            showSearchPercentage(100, '#list_members_my_conversations .headline');
            oldestUnreadMessagesPage(lastUnreadPage);
            // console.log("Returning", lastUnreadPage)
            return lastUnreadPage;
        }


        async function checkAndFixMessages() {
            const currentPage = getCurrentPage();
            const data = await getConversationsWithUnreadStatus(currentPage);

            let hasNewElements = false;

            data.forEach(item => {
                let userID = extractUserId(item.href)

                const userItem = document.querySelector(`#list_members_my_conversations_items > div.item > a[href="https://www.camwhores.tv/my/messages/${userID}/"]`);
                if (userItem) {
                    const unreadNotification = userItem.querySelector('div.img .unread-notification');
                    if (unreadNotification && !item.unread) {
                        unreadNotification.remove();
                        if (DEBUG) console.log("Message with", item.name, "was read");
                    }
                } else {
                    hasNewElements = true;
                }
            });

            if (hasNewElements && !readingMessages) {
                location.reload();
            }
        }




        GM_addStyle(`
#list_members_my_conversations_items div > a{
  overflow:hidden;
}
#list_members_my_conversations_items div > a > div.img > a.open-profile{
  position: absolute;
  top:50%;
  right:-.5rem;
  transform: translateY(-50%);
  opacity:0;
  transition: .4s;
  font-size:1.75rem;
  pointer-events: none;
  padding:1rem .4rem;
}
#list_members_my_conversations_items div > a > div.img > .open-profile{
  filter: sepia(1) hue-rotate(330deg) saturate(2) brightness(1.1);
  transition: .2s;


}
#list_members_my_conversations_items div > a > div.img > .open-profile:hover{
  filter:sepia(.9) hue-rotate(320deg) saturate(1.8) brightness(.9) contrast(1.1)
}

#list_members_my_conversations_items div > a > div.img:hover > a.open-profile{
  opacity:1;
  pointer-events: all;
}

#list_members_my_conversations_items div > a > div.img img{
  transform: scale(1);
  transform-origin: center;
  transition: .3s;
}
#list_members_my_conversations_items div > a > div.img:hover img{
  filter: blur(10px) brightness(.8) contrast(1.1);
  transform: scale(1.1);
}
#list_members_my_conversations_items div > a > div.img span.no-thumb{
  transition: opacity 1s
}
#list_members_my_conversations_items div > a > div.img:hover span.no-thumb{
  opacity:.5;
  transition: opacity .4s;
}
`);



        async function processThumnailMsgPreviews(force = false) {
            // console.log("processThumnailMsgPreviews()")
            const items = document.querySelectorAll('#list_members_my_conversations_items .item');
            if (!items.length) return;

            for (const item of items) {
                if (!force && item?.dataset.processed === 'true') return;

                const thumbnailText = item.querySelector('strong.title')?.textContent.trim();
                if (!thumbnailText) continue;

                const messageURL = item.querySelector('a');
                const contentContainer = messageURL?.querySelector('div.img');
                const href = messageURL?.getAttribute('href');
                const userId = href?.match(/\/(\d+)\/$/)?.[1];
                if (!userId) continue;

                const htmlDoc = await fetchHTML(`https://www.camwhores.tv/members/${userId}/`);
                // console.log("fetched",userId);

                const infoMessage = htmlDoc.querySelector('.info-message');
                if (infoMessage?.textContent.includes("is in your friends list.")) {
                    let divElement = Object.assign(document.createElement('div'), {
                        className: "friends-tag",
                        textContent: "Friends ✅",
                        style: `position:absolute;top:0;right:0;background-color:#414141;border-bottom-left-radius:3px;color:white;padding:3px 5px;`
                    });
                    contentContainer?.appendChild(divElement);
                }

                let bottomLeftCorner = Object.assign(document.createElement('div'), {
                    style: `position:absolute;bottom:0;left:0;color:white;font-size:1rem;padding:.5rem .25rem;`
                });
                contentContainer?.appendChild(bottomLeftCorner);



                if (infoMessage?.textContent.includes("wants to be your friend.")) {
                    let divElement = Object.assign(document.createElement('span'), {
                        textContent: `👀`,
                        title: "Wants to be your friend!"
                    });
                    bottomLeftCorner?.appendChild(divElement);
                }

                let starredUser = retrieveValueFromStorage("favourite_users")[userId]?.friends;
                if (starredUser) {
                    let star = Object.assign(document.createElement('span'), {
                        textContent: '★',
                        title: 'Starred user',
                        className: 'cum_star_user',
                        style: `font-size:1.5rem;margin:0`
                    });
                    bottomLeftCorner?.appendChild(star);
                }




                let openProfile = Object.assign(document.createElement('a'), {
                    className: "open-profile",
                    textContent: '👤',
                    title: `Open ${thumbnailText}'s profile`,
                    href: `/members/${userId}/`
                });
                contentContainer?.appendChild(openProfile);


                let videoNumber = htmlDoc.querySelector('#list_videos_uploaded_videos div.headline h2')?.textContent.match(/\(([^)]+)\)/)?.[1] || 0;
                let divElement = Object.assign(document.createElement('div'), {
                    className: "videos-tag",
                    textContent: videoNumber,
                    style: `position:absolute;bottom:0;right:0;background-color:CadetBlue;color:white;padding:3px 5px;border-top-left-radius:3px;`
                });
                if (videoNumber === 0) divElement.style.backgroundColor = 'hsla(0deg, 51.63%, 30%, 50%)';
                contentContainer?.appendChild(divElement);


                item.dataset.processed = 'true';
            }
            return true;
        }






        async function addButtonEventListener() {
            document.addEventListener('click', function (event) {
                var target = event.target;
                if (target && target.matches('a[data-action="ajax"]')) {

                    var h2Element = document.querySelector('#list_members_my_conversations div.headline h2');
                    const initialText = h2Element.textContent.trim();

                    const checkChangeInterval = setInterval(function () {
                        h2Element = document.querySelector('#list_members_my_conversations div.headline h2');
                        const currentText = h2Element.textContent.trim();

                        if (currentText !== initialText) {
                            clearInterval(checkChangeInterval);
                            processThumnailMsgPreviews();
                        }
                    }, 100);
                }
            });
        }

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

        setInterval(function () {
            processThumnailMsgPreviews()
        }, 5000);

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

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

            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', () => {
                    loadListDynamically('list_members_my_conversations', myMessagesAsyncURL(getCurrentPage()), getCurrentPage(), () => processThumnailMsgPreviews(true))
                });
                h2Element.appendChild(refreshIcon);
            }

            if (!document.getElementById('lastUnreadButton')) {
                let title = `Go to oldest unread page`;

                let lastUnreadButton = document.createElement('a');
                lastUnreadButton.id = 'lastUnreadButton';
                lastUnreadButton.innerHTML = '⏭';
                lastUnreadButton.style = 'cursor:pointer;margin-left:10px;text-decoration:none;';
                lastUnreadButton.addEventListener('click', async () => {

                    lastUnreadButton.classList.add('fade');
                    readingMessages = true;
                    let hasUnread;

                    let currentPage = getCurrentPage();
                    let oldestUnreadPage = 0;
                    let savedUnreadPage = oldestUnreadMessagesPage();


                    if (savedUnreadPage > currentPage) {
                        let data = await getConversationsWithUnreadStatus(savedUnreadPage);
                        hasUnread = data.some(message => message.unread);

                        oldestUnreadPage = hasUnread ? savedUnreadPage : 0;


                    }


                    if (oldestUnreadPage <= 0) {

                        // console.log(" if(!oldestUnreadPage) oldestUnreadPage"+oldestUnreadPage)


                        let startFrom = currentPage;
                        startFrom += 1;
                        let lastPageElement = document.querySelector("#list_members_my_conversations_pagination > div > ul > li.last a");
                        let lastPage = !!lastPageElement ? parseInt(lastPageElement.getAttribute('href').match(/\/(\d+)\/$/)?.[1], 10) : 1;





                        oldestUnreadPage = await lastUnreadPage(currentPage, lastPage);


                        // if((oldestUnreadPage === 0 || oldestUnreadPage <= currentPage)){
                        //   console.log("I would enter the while loop because oldestUnreadPage",oldestUnreadPage, "===",0,"or (oldestUnreadPage <= currentPage)", oldestUnreadPage, "<=" ,currentPage)
                        // }


                        while (oldestUnreadPage === 0 || oldestUnreadPage <= currentPage) {
                            startFrom += 10;

                            if (startFrom >= lastPage) break;

                            console.log("Checking pages", startFrom, "-", startFrom + maxPagesMessagesSearch, "Last page:", lastPage)
                            oldestUnreadPage = await lastUnreadPage(startFrom, lastPage);
                        }


                    }


                    // console.log("oldestUnreadPage", oldestUnreadPage)
                    lastUnreadButton.classList.remove('fade');
                    readingMessages = false;


                    loadListDynamically(
                        'list_members_my_conversations',
                        myMessagesAsyncURL(oldestUnreadPage),
                        oldestUnreadPage,
                        () => processThumnailMsgPreviews(true)
                    );






                });

                lastUnreadButton.title = title;
                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 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
        let submitForm = document.querySelector('input.submit');
        let editButtons = document.querySelectorAll('.item .editable');

        if (sessionStorage.getItem('go_to_uploaded_videos') === 'true') {
            sessionStorage.removeItem('go_to_uploaded_videos');
            if (history.length === 1) window.location.href = `/members/${userID}/#list_videos_my_uploaded_videos`;
        }
        if (sessionStorage.getItem('return_to_messages') === 'true') {
            sessionStorage.removeItem('return_to_messages');
            if (window.history.length) window.location.href = `/my/messages/`;
            //send_done

        }

        confirmButton?.addEventListener('click', () => sessionStorage.setItem('go_to_uploaded_videos', 'true'));
        rejectButton?.addEventListener('click', () => sessionStorage.setItem('return_to_messages', 'true'));



        let debugTextarea = false;
        debugTextarea ? textarea.placeholder = "OLD, original" : textarea.style.display = "none";

        let divTextarea = document.createElement('div');
        divTextarea.classList.add("textarea");
        divTextarea.id = 'custom-textarea';
        divTextarea.contentEditable = true;
        textarea.parentNode.insertBefore(divTextarea, textarea.nextSibling);

        //         function copyContentToTextarea() {
        //             const content = Array.from(divTextarea.childNodes).map(node => {
        //                 if (node.nodeType === 3) return node.textContent; // Handles plain text
        //                 if (node.tagName === 'IMG') return node.alt;
        //                 if (node.tagName === 'DIV') return '\n' + node.textContent;
        //                 if (node.tagName === 'BR') return '\n';
        //                 return '';
        //             }).join('').trim();

        //             textarea.value = content;
        //         }

        function copyContentToTextarea() {
            const content = Array.from(divTextarea.childNodes).map(node => {
                if (node.nodeType === 3) return node.textContent;
                if (node.tagName === 'IMG') return node.alt;
                if (node.tagName === 'DIV') return '\n' + node.textContent;
                if (node.tagName === 'BR') return '\n';
                return node.textContent || '';
            }).join('').trim();

            textarea.value = content;
        }



        editButtons.forEach(element => {
            element.addEventListener('click', function () {
                console.log("textarea content:", textarea.value);
                setTimeout(() => {
                    const formattedContent = textarea.value.replace(/\n/g, '<br>');
                    divTextarea.innerHTML = formattedContent;
                }, 0);
            });
        });



        divTextarea.oninput = copyContentToTextarea;
        submitForm?.addEventListener('click', () => divTextarea.innerHTML = '');



        document.addEventListener('keydown', function (event) {
            if (event.ctrlKey && event.key === 'Enter') {
                event.preventDefault();
                console.log("Ctrl+Enter pressed");
                document.querySelector('#send_message_form').submit();
            }
        });



        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();
            }
        );


        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) + '/';

                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));

            }
        });

        function updateStar(starEmoji) {
            let userData = retrieveValueFromStorage("favourite_users")[userID];

            starEmoji.textContent = userData?.friends ? '★' : '☆';
            starEmoji.classList.toggle('no', !userData?.friends);

            starEmoji.title = userData?.friends ? 'Remove star' : 'Add star';

            starEmoji.onmouseover = () => {
                starEmoji.textContent = starEmoji.textContent === '★' ? '☆' : '★';
            };
            starEmoji.onmouseout = () => {
                starEmoji.textContent = userData?.friends ? '★' : '☆';
            };
        }


        function starListener(starEmoji) {
            starEmoji.addEventListener('click', () => {
                let oldData = retrieveValueFromStorage("favourite_users");
                // console.log(oldData)

                let friends = oldData?.[userID]?.friends || false;
                let firstAdd = oldData?.[userID]?.firstAdd || null;

                let data = {
                    update: Date.now(),
                    id: userID,
                    friends: !friends,
                    firstAdd: firstAdd || Date.now()
                };
                saveArraySettings("favourite_users", userID, data);
                updateStar(starEmoji);

            });
        }
        function addStarButton() {
            let titleHeadline = document.querySelector('#list_messages_my_conversation_messages .headline h2');

            let starEmoji = document.querySelector('#cum_star_user');
            if (!!starEmoji) return;

            starEmoji = document.createElement('span');
            starEmoji.id = 'cum_star_user';

            updateStar(starEmoji);
            starListener(starEmoji);

            titleHeadline.appendChild(starEmoji);
        }

        addStarButton()






        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());
            denyClose.setAttribute('data-action', 'denyclose')

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

            return { confirmClose, denyClose };
        }



        function encodeHTML(str) {
            return str.replace(/&/g, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                .replace(/"/g, '&quot;')
                .replace(/'/g, '&#39;');
        }

        function updateMessageDOM(htmlDoc, confirmClose, denyClose) {
            console.log(htmlDoc)
            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)

            let userDescription = htmlDoc.querySelector("body > div.container > div.content > div > div.main-container-user > div.block-profile > div > div > div.about-me > div > em")?.textContent.trim();

            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';
                    let encodedDescription = encodeHTML(userDescription);
                    let aboutUserSection = encodedDescription !== 'no info'
                        ? `<br><br><b>About user:</b><div style="white-space:pre-wrap">${encodedDescription}</div>`
                        : '';

                    generatedTextDiv.innerHTML = `<div>
                                                    ${extractedTitle[1]} videos, last ${lastUploadTime}<br>Joined ${joinedDateText}
                                                    ${aboutUserSection}
                                                  </div>`;

                    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);
                            imgDiv.appendChild(imgLink);
                            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);
            fetchMessagesAndExtract(messageUrl, function (htmlDoc) {
                var closeButtons = addCloseButtonsListeners(confirmButton, rejectButton);
                var confirmClose = closeButtons.confirmClose;
                var denyClose = closeButtons.denyClose;
                updateMessageDOM(htmlDoc, confirmClose, denyClose);

            });
        }


        // 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");
        }



        document.querySelectorAll('.message-text').forEach(div => {
            if (div.textContent.includes('has denied your invitation.')) {
                div.parentElement.style.backgroundColor = '#8b4949'; // Red shade
            } else if (div.textContent.includes('has confirmed your invitation')) {
                div.parentElement.style.backgroundColor = '#4a7c59'; // Green shade
            }
        });


    }

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


        function getPrivateVideos() {
            const private_videos = [];

            const videoElements = document.querySelectorAll('.item.private');

            videoElements.forEach(video => {
                let parentDiv = video.parentElement;
                if (
                    parentDiv.tagName === 'DIV' &&
                    parentDiv.id !== 'list_albums_albums_list_search_result_items' &&
                    !video.hasAttribute('data-bg-checked')
                ) {
                    video.setAttribute('data-bg-checked', 'true');
                    // video.style.outline ="3px solid purple"
                    private_videos.push(video);
                }
            });

            return private_videos;
        }

        async function checkVideoPage(url) {
            try {
                let asyncURL = url + '?mode=async&function=get_block&block_id=video_view_video_view&_=' + Date.now();

                const htmlDoc = await retryUntilSuccess(() => fetchHTML(asyncURL), 5000);

                let message = htmlDoc.querySelector('.message');
                let isByFriend = !!message && message.innerText.includes('This video is a private video');

                let usernameElement = htmlDoc.querySelector('#tab_video_info .block-user .username a');
                let username = !!usernameElement ? usernameElement.innerText.trim() : '';
                let userUrl = usernameElement ? usernameElement.href : '';

                let userID = extractUserId(userUrl);

                return { byFriend: isByFriend, userID: userID, username: username };

            } catch (error) {
                console.error('Error checking video page:', error);
                return { byFriend: false, userID: null };
            }
        }

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

        async function applyStyleToPrivateVideos() {
            if (advancedOptions['disableBackgroundFetching']) {
                if (DEBUG) console.log("Returning earlier because advanced disableBackgroundFetching =", advancedOptions['disableBackgroundFetching'])
                return;
            }

            if (advancedOptions['backgroundFetchOnlyVideos'] && !/\/(tags|search)\//.test(window.location.href)) {
                console.log("Returning because of advanced setting backgroundFetchOnlyVideos");
                return;
            }

            while (document.hidden) {
                await sleep(250);
            }

            for (const video of getPrivateVideos()) {
                const videoA = video.querySelector('a');

                const img = videoA.querySelector('div > img');
                const link = videoA.href;

                if (!link) continue;

                const { byFriend, userID, username } = await checkVideoPage(link);

                if (alternative_thumbnails_users.includes(username)) updateImageSrc(img);

                if (!byFriend) {
                    stylePrivateVideo(videoA, img);
                    continue;
                }

                await handleFriendRequest(videoA, userID, username, img, link);
            }
        }

        function stylePrivateVideo(video, img) {
            video.style.outline = 'green solid 2px';
            const linePrivate = video.querySelector('.line-private');
            if (linePrivate) linePrivate.style.display = 'none';

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

        async function handleFriendRequest(video, userID, username, img, link) {
            const linePrivate = video.querySelector('.line-private > span');
            linePrivate.textContent += ' ❌';

            if (!linePrivate || alreadySentRequest(userID)) return;

            const addFriend = createFriendRequestLink(userID);
            addFriend.addEventListener('mousedown', e => {
                e.stopImmediatePropagation();
                friendRequestSetup(video.querySelector('strong.title').textContent.trim(), link, userID);
            });

            video.querySelector('.img').appendChild(addFriend);

        }

        function createFriendRequestLink(usernameID) {
            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);'
            });
            return addFriend;
        }

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

        applyStyleToPrivateVideos();
        highlightKeywords();

        setTimeout(() => {
            let lastRun = 0;

            const videosObserver = new MutationObserver(() => {
                const now = Date.now();
                if (now - lastRun >= 500) {
                    lastRun = now;
                    applyStyleToPrivateVideos();
                    highlightKeywords();
                }
            });

            videosObserver.observe(document.body, { childList: true, subtree: true });
        }, 1000);




    }

    if (window.location.href.startsWith("https://www.camwhores.tv/members/")) {
        const userId = window.location.href.split('/').slice(-2, -1)[0];
        let 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 numberBetweenParenthesis(text) {
            return text?.match(/\((\d+)\)/)?.[1] ?? null;
        }

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

        function addListInfo(selector = '#list_videos_uploaded_videos .headline h2') {
            let listTitle = document.querySelector(selector);
            if (!listTitle) {

                return;
            }
            if (listTitle.textContent.includes("/") || listTitle.textContent.includes("pages")) return;

            let roundedPages = getRoundedTotalPages(listTitle);
            if (!roundedPages) return;

            if (listTitle.innerHTML.includes("Page")) listTitle.innerHTML = listTitle.innerHTML.trim() + ` / ${roundedPages}`;
            if (!listTitle.innerHTML.includes("Page")) listTitle.innerHTML = listTitle.innerHTML.trim() + `, in ${roundedPages} pages`;

        }


        function addSearchButton(listId = 'list_videos_uploaded_videos', asyncURL = userVideosAsyncURL) {
            const searchSelector = `#cum_${listId}_search`;
            if (document.querySelector(searchSelector)) return;

            const sortElement = document.querySelector(`#${listId} > div.headline > div.sort`);
            if (!sortElement) return;

            const existingButton = document.querySelector(searchSelector);
            if (!!existingButton) return;

            const searchButton = document.createElement('div');
            searchButton.className = 'user-search';
            searchButton.innerHTML = '<strong>Search videos</strong><span class="icon type-search"></span>';
            searchButton.id = searchSelector.substring(1); // Remove the # for the ID
            searchButton.addEventListener('click', () => searchDialog(listId, asyncURL));
            sortElement.parentNode.insertBefore(searchButton, sortElement);


        }

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

            let searchHighlights = retrieveValueFromStorage("highlights_search") || [];

            let dialog = document.createElement('div');
            dialog.id = "search-dialog";
            dialog.innerHTML = `
                    <div class="fancybox-item fancybox-close" style="position:absolute;top:5px;right:5px;"></div>
                <form style="padding-top:10px" id="cum_form_query">
                    <input type="text" autofocus placeholder="Search... 🔍" style="margin-right:5px;padding:5px;">
                    <button class="btn" type="submit" style="padding:5px;box-sizing:border-box;height:30px;cursor:pointer;">Search</button>
                </form>
                <hr style="margin-top:10px;border:none;height:1px;background-color:rgb(58, 63, 65);">
                <button class="btn" 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="${highlight_keywords.join(", ")}">📚 Search <strong>every</strong> highlight word</button>
                    <button class="btn" id="cum_search_checkbox" type="button" style="display:block;padding:5px;box-sizing:border-box;height:30px;cursor:pointer;width:100%;margin-top:10px">⬇️ Search selected</button>
                <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('')}

                </form>
            `;

            document.body.insertBefore(dialog, document.body.firstChild);

            dialog.querySelector('#cum_search_checkbox').addEventListener('click', e => {
                e.preventDefault();

                let selectedKeywords = Array.from(dialog.querySelectorAll('input[type="checkbox"]:checked'))
                    .map(checkbox => checkbox.name);

                saveValue("highlights_search", selectedKeywords)
                displayResults(selectedKeywords, listId, asyncURL);

                document.body.removeChild(dialog);
            });




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



        async function displayResults(query, listId, asyncURL) {
            try {
                const resultList = document.createElement('div');
                // resultList.id = 'cum_search_results'; // ???????? change
                resultList.className = "list-videos";


                let results = await videoSearch(userId, query, asyncURL, listId);

                resultList.innerHTML = `<div class="headline" style="padding:15px 5px 7px 5px;"><h2>Search results</h2></div>`;

                await sleep(250)

                if (DEBUG) console.log(results);

                if (DEBUG) console.log(Array.isArray(results));
                if (!results.length) {
                    resultList.textContent = "No results found";
                    resultList.style.marginTop = "1rem";

                } 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(`#${listId} + .list-videos`);
                if (existingResult) existingResult.replaceWith(resultList);
                else document.querySelector("#" + listId).insertAdjacentElement('afterend', resultList);
            } catch (error) {
                console.error("Error:", error);
            }
            // console.log("Finished ")
        }


        async function getSearchTerms(query) {
            return Array.isArray(query)
                ? query.map(q => q.toLowerCase())
                : query
                    ? [query.toLowerCase()]
                    : highlight_keywords.map(k => k.toLowerCase());
        }

        function extractVideoDetails(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');
            return { title, private: isPrivate, url, imgSrc, duration };
        }

        function filterVideosBySearchTerms(videoDetails, searchTerms) {
            const titleInLowerCase = videoDetails.title.toLowerCase();

            const isMatchFound = searchTerms.some(searchTerm => {
                const searchTermInLowerCase = searchTerm.toLowerCase();

                // console.log(`Checking if "${searchTerm}" is in the title '${titleInLowerCase}': ${titleInLowerCase.includes(searchTermInLowerCase)}`);

                return titleInLowerCase.includes(searchTermInLowerCase);
            });

            return isMatchFound;
        }


        function getVideoDetailsFromHTML(videos, searchTerms, resultArray) {
            videos.forEach(video => {
                let videoDetails = extractVideoDetails(video);
                if (filterVideosBySearchTerms(videoDetails, searchTerms)) {
                    resultArray.push(videoDetails);
                }
            });
        }


        async function fetchVideosFromPage(userId, pageInt, asyncURL, listId, searchTerms, resultArray) {
            let nextFivePages = asyncURL(userId, pageInt);
            // console.log("nextFivePages:", nextFivePages);

            let success = false;
            while (!success) {
                try {
                    let htmlDoc = await fetchHTML(nextFivePages);  // Await fetchHTML without a callback

                    let videos = htmlDoc.querySelectorAll(`#${listId} div.item`);

                    getVideoDetailsFromHTML(videos, searchTerms, resultArray);
                    success = true;  // Only set success to true if parsing and extraction succeed

                } catch (error) {
                    console.log("Error fetching HTML:", error);
                    await sleep(5000);  // Retry after delay
                }
            }
        }



        async function videoSearch(userId, query, asyncURL, listId) {
            let searchTerms = await getSearchTerms(query);

            let videosH2 = document.querySelector(`#${listId} .headline h2`);
            let totalVideoPages = getRoundedTotalPages(videosH2);
            let approxTotalVideos = totalVideoPages * 5;
            let timeout = approxTotalVideos > 500 ? approxTotalVideos / 5 : 75;

            if (totalVideoPages === null) {
                console.error("ERROR in totalVideoPages:", totalVideoPages);
                return null;
            }

            let resultArray = [];
            for (let pageInt = 1; pageInt <= totalVideoPages; pageInt++) {
                let percentage = pageInt / totalVideoPages * 100;
                showSearchPercentage(percentage, `#${listId}_pagination`);

                await sleep(timeout); // Proper delay between each request
                await fetchVideosFromPage(userId, pageInt, asyncURL, listId, searchTerms, resultArray);
            }

            return resultArray;
        }






        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";
        }

        new MutationObserver(() => {
            addSearchButton('list_videos_uploaded_videos', userVideosAsyncURL);
            addSearchButton('list_videos_favourite_videos', favouriteVideosAsyncURL);

            addListInfo('#list_videos_uploaded_videos .headline h2');
            addListInfo('#list_videos_favourite_videos .headline h2');
            addListInfo('#list_members_friends .headline h2');
        }).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')) || {};
            if (DEBUG) console.log("uid:", userId, retrievedData[userId])
            if (retrievedData[userId]) {
                userVideoData = retrievedData[userId];
                lastCumTitle = userVideoData.title;
                lastVidCode = userVideoData.videoId;
                lastUserID = userVideoData.userId;
            } else {
                if (DEBUG) 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();
        }

        async function ornateCumUser(id) {
            while (document.visibilityState !== 'visible') {
                await sleep(250);
            }
            let userExists = await cumUser(id);
            if (!userExists) return;

            h1.innerHTML += ` <span title="CUM user" style="cursor:default">👑</span>`;
        }
        ornateCumUser(userId);


    }

    if (window.location.href.startsWith("https://www.camwhores.tv/videos/")) {

        if (document.body.textContent.includes('The video you’re trying to view has been taken down in compliance with a DMCA')) return;

        let privateVideoMessage = 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');
        let uploader = document.querySelector('#tab_video_info > div > div.block-user > div.username > a');
        let uploaderHref = uploader.href;

        let headline = document.querySelector("body > div.container > div.content > div.headline");
        let h1 = headline.querySelector("h1");
        let title = h1.innerText;

        let uploaderId = extractUserId(uploaderHref);
        let alreadySentFriendshipRequest = alreadySentRequest(uploaderId);
        let stringDuration = document.querySelector("#tab_video_info > div > div.info > div:nth-child(1) > span:nth-child(1) > em").textContent.trim();
        let views = document.querySelector("#tab_video_info > div > div.info > div:nth-child(1) > span:nth-child(2) > em").textContent.trim();
        let submitted = document.querySelector("#tab_video_info > div > div.info > div:nth-child(1) > span:nth-child(3) > em").textContent.trim();
        let description = document.querySelector("#tab_video_info > div > div.info > div:nth-child(2) > em").textContent.trim();
        let categories = Array.from(document.querySelectorAll("#tab_video_info > div > div.info > div:nth-child(3) a")).map(el => el.innerText.trim()).join(",");
        let tags = Array.from(document.querySelectorAll("#tab_video_info > div > div.info > div:nth-child(4) a")).map(el => el.innerText.trim()).join(",");
        let models = Array.from(document.querySelectorAll("#tab_video_info > div > div.info > div:nth-child(5) a")).map(el => el.innerText.trim()).join(",");
        let rating = document.querySelector("div.video-info > div > div.info-buttons > div.rating-container > div > span.scale-holder > span").getAttribute('data-rating') || null;

        let screenshot = document.querySelector("#tab_screenshots > div.block-screenshots > a:nth-child(1) > img") || document.querySelector("#tab_screenshots > div > span:nth-child(1) > img");
        let screenshotSrc = screenshot?.getAttribute('src');
        let screenshotDataSrc = screenshot?.getAttribute('data-original');
        let screenshotUrl = screenshotSrc?.includes('https') ? screenshotSrc : screenshotDataSrc?.includes('https') ? screenshotDataSrc : null;
        let screenshotId = extractScreenshotId(screenshotUrl);




        let titleReplacements = { '3': 'e', '@': 'a', '1': 'i', '0': 'o', '$': 's', '7': 't', '4': 'a', '5': 's', '6': 'g', '!': 'i' };
        let specialCharReplacements = { 'İ': 'I', 'ı': 'i', 'ş': 's', 'ç': 'c', 'ğ': 'g', 'ö': 'o', 'ü': 'u', 'æ': 'ae', 'œ': 'oe', 'ñ': 'n', 'ß': 'B' };

        // function decodeTitle(title) {
        //     return title.normalize('NFD')
        //         .replace(/[\u0300-\u036f]/g, '')
        //         .replace(/[^\w\s]|[0-9]/g, char => specialCharReplacements[char] || titleReplacements[char] || char)
        //         .replace(/\s+/g, ' ');
        // }
        function decodeTitle(title) {
            return title.normalize('NFD')
                .replace(/[\u0300-\u036f]/g, '')
                .replace(/[0-9@!$]/g, char => titleReplacements[char] || char) // Handle title replacements first
                .replace(/[^\w\s]/g, char => specialCharReplacements[char] || char) // Then handle special chars
                .replace(/\s+/g, ' ');
        }





        let decodedTitle = decodeTitle(title);

        if (decodedTitle !== title) {
            let label = document.createElement('label');
            label.classList.add('switch-label');


            label.innerHTML = `<div class="switch"><input type="checkbox" class="toggle" id="decodeTitle"><span class="slider round"></span></div> Decode title`;
            headline.appendChild(label);

            let toggle = label.querySelector(`#decodeTitle`);
            toggle.addEventListener('change', function () {
                if (this.checked) {
                    h1.innerText = decodedTitle;
                } else {
                    h1.innerText = title;

                }
            });

        }


        if (history.length === 1) {
            let close = document.createElement('button');
            close.innerText = "Close";
            close.style.float = "right";
            close.style.userSelect = "none";
            close.style.cursor = "pointer";
            close.onclick = function () {
                window.close();
            };
            headline.appendChild(close);


        }



        function extractScreenshotId(url) {
            const regex = /(\d+\/\d+)/;
            return url.match(regex)[0];
        }


        sendData({
            video: {
                uploader: uploaderId,
                title: title,
                videoId: extractVideoId(window.location.href),
                duration: timeStringToSeconds(stringDuration),
                views: parseInt(views.replace(/\s+/g, '')),
                submitted: secondsToTimestamp(-1 * convertToSeconds(submitted), false),
                description: description,
                categories: categories || null,
                tags: tags || null,
                models: models || null,
                rating: +parseFloat(rating).toFixed(4) || null,
                thumbnail: screenshotId
            }
        });


        if (privateVideoMessage) {
            fetch(uploaderHref)
                .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) {
                        privateVideoMessage.textContent = "You're already friends! Reloading in 4 seconds";
                        setTimeout(() => location.reload(), 4000);
                    } else if (isPending) {
                        privateVideoMessage.innerHTML += "<br><br>Friendship confirmed? Reloading in 4 seconds";
                        setTimeout(() => location.reload(), 4000);
                    } else if (!!headline) {
                        if (alreadySentFriendshipRequest && history.length === 1 && !document.hasFocus()) {
                            window.close();
                            return;
                        }

                        if (!alreadySentFriendshipRequest && autoFriendshipRequest && (history.length === 1 || !document.hasFocus())) {
                            window.location.href = `${privateVideoMessage.querySelector('a').href}#friends`;
                            return;
                        }

                        friendRequestSetup(headline.textContent.trim(), window.location.href, uploaderId);
                        let alreadySentString = alreadySentFriendshipRequest ? ` (Already sent)` : ``;
                        privateVideoMessage.innerHTML += `<br><a class="button" href="${privateVideoMessage.querySelector('a').href}#friends" style="display:inline-block;">Send request${alreadySentString}</a>`;

                    }
                    let blockedSS = document.querySelectorAll('.block-screenshots .item.private');
                    blockedSS.forEach(ss => {
                        const img = ss.querySelector('img');
                        if (img) {
                            let fullImg = img.dataset.original.replace('/videos_screenshots/', '/videos_sources/').replace('/180x135/', '/screenshots/');
                            img.src = fullImg;
                            img.style.width = "auto";
                            ss.replaceWith(Object.assign(document.createElement('a'), {
                                innerHTML: ss.innerHTML,
                                href: fullImg,
                                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:0.2rem;align-items:center;justify-content:center;margin-top:.5rem;';
            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.classList.add('btn');
                button1.addEventListener('click', function () {
                    flowplayer().seek(timestamp);
                });
                timestampsDiv.appendChild(button1);

                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.classList.add('btn');
                    button2.addEventListener('click', function () {
                        event.preventDefault();
                        event.stopPropagation();
                        flowplayer().seek(timestamp);
                        if (DEBUG) console.log("Going to", formattedTimestamp);
                    });
                    screenshotLinks[index].style.position = 'relative';
                    screenshotLinks[index].appendChild(button2);
                }
            });



        }
        function waitForVideoDuration(intervalMs) {
            const percentages = [25, 40, 59.75, 79];
            const firstTimestamp = 14;

            function checkDuration() {
                const player = typeof flowplayer === 'function' ? flowplayer() : null;

                if (player && player.video && typeof player.video.duration === 'number') {
                    flowplayer().seek("4");

                    const videoDuration = player.video.duration;

                    const calculatedPercentages = percentages.map(percent => (percent / 100) * videoDuration);

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

                    const timestamps = [firstTimestamp, ...calculatedPercentages];
                    addTimestampsToVideo(timestamps);

                } else {
                    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(500);

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



        async function videoDoubleClickFullscreen() {
            await waitForPageLoad();
            if (!document.querySelector('#kt_player')) return;
            document.querySelector('#kt_player').addEventListener('dblclick', function (event) {
                flowplayer().fullscreen()
            });
        }
        videoDoubleClickFullscreen()

    }

    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 currentSearchTerm(param = 'search') {
        const pathSegment = window.location.pathname.split(`/${param}/`)[1]?.split('/')[0].trim();
        if (!pathSegment) return null;

        return param === 'tags'
            ? pathSegment.replace(/-/g, '_').toLowerCase()
            : pathSegment.replace(/%20|-/g, ' ').replace(/\s+/g, ' ').toLowerCase();
    }



    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 updateLatestVideo(searchTerm = null, videoId = extractLatestVideoId()) {
        if (!searchTerm) searchTerm = currentSearchTerm();
        if (!searchTerm) return;
        if (DEBUG) console.log("updateLatestVideo. searchTerm:", searchTerm, "videoId:", videoId)
        let data = {
            timestamp: Date.now(),
            id: videoId
        };
        return saveArraySettings("auto_search", searchTerm, data);
    }
    function removeFromWishlist(searchTerm) {
        let newWishlistVideos = loadArrayLocalStorage('newWishlistVideos');
        let updatedWishlist = newWishlistVideos.filter(video => video.query !== searchTerm);
        if (updatedWishlist.length !== newWishlistVideos.length) saveArrayLocalStorage('newWishlistVideos', updatedWishlist);
    }

    function addDeleteFromWishlistBtn(searchTerm, addToWishListBtn) {
        let deleteFromWishList = document.createElement('button');
        deleteFromWishList.textContent = 'Remove';
        deleteFromWishList.id = 'deleteFromWishList';
        deleteFromWishList.classList.add('btn');
        deleteFromWishList.style = "transform:translate(5px, 3px);margin-left:5px;"
        deleteFromWishList.addEventListener('click', () => {

            (async () => {
                const mainKey = "auto_search";
                const entryKey = searchTerm;

                await removeEntryFromStorage(mainKey, entryKey);

                // let updatedValue = await GM.getValue(mainKey);
                // console.log("Updated value:", updatedValue);
            })();


            deleteFromWishList.textContent = 'Done';
            deleteFromWishList.disabled = true;
            addToWishListBtn.textContent = 'Add to Wish List';
            addToWishListBtn.disabled = false;
            deleteFromWishList.remove()


        });
        document.querySelector('#add-to-wish-list')?.insertAdjacentElement('afterend', deleteFromWishList); //
    }


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

        let tag = currentSearchTerm('tags');
        let addToWishListBtn = document.querySelector('search-tag');
        if (!addToWishListBtn) {
            addToWishListBtn = document.createElement('button');
            addToWishListBtn.textContent = 'Search tag';
            addToWishListBtn.id = 'search-tag';
            addToWishListBtn.classList.add('btn');
            addToWishListBtn.style = "transform:translate(5px, 3px);cursor:pointer"
            addToWishListBtn.addEventListener('click', () => {
                updateLatestVideo();

                window.location.href = `/search/${tag}/`
                addToWishListBtn.disabled = true;
            });

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

    if (window.location.pathname.startsWith('/search/')) {
        let searchTerm = currentSearchTerm();
        let addToWishListBtn = document.createElement('button');
        addToWishListBtn.textContent = 'Add to Wish List';
        addToWishListBtn.id = 'add-to-wish-list';
        addToWishListBtn.classList.add('btn');
        addToWishListBtn.style = "transform:translate(5px, 3px);cursor:pointer"
        addToWishListBtn.addEventListener('click', () => {
            updateLatestVideo();

            addToWishListBtn.textContent = 'Done!';
            addToWishListBtn.disabled = true;
            addDeleteFromWishlistBtn(searchTerm, addToWishListBtn);
            // console.log("did we add?")

        });

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

        let searchInStorage = retrieveValueFromStorage("auto_search")[searchTerm];
        if (searchInStorage) {
            updateLatestVideo()
            addToWishListBtn.disabled = true;

            addDeleteFromWishlistBtn(searchTerm, addToWishListBtn);
        }

        removeFromWishlist(searchTerm)




    }

    let searchBox = document.querySelector(`div.search`);
    if (!!searchBox) {

        let cumSearchDiv = document.createElement('div');
        cumSearchDiv.id = 'CUM_search_bg';
        cumSearchDiv.onclick = closeCUMdialog;
        document.body.appendChild(cumSearchDiv);

        searchBox.innerHTML += `<label class="switch-label">
        <div class="switch">
          <input type="checkbox" id="cum_search_checkbox">
          <span class="slider round"></span>
          </div> CUM search</label>`;


        let cumSearch = document.querySelector('#cum_search_checkbox');
        let searchForm = document.querySelector('#search_form');
        let searchButton = searchForm.querySelector('.search-button');

        searchButton.addEventListener('click', () => {
            // console.log(cumSearch.checked, )
            event.preventDefault();
            let textInput = searchForm.querySelector('input[name="q"]');
            let inputValue = textInput.value.trim();
            if (cumSearch.checked) {
                buildCUMSearch(inputValue);
            } else {
                window.location.href = `/search/${inputValue}/`
            }
        });

        searchForm.addEventListener('submit', (event) => {
            event.preventDefault();
            let textInput = searchForm.querySelector('input[name="q"]');
            let inputValue = textInput.value.trim();
            if (cumSearch.checked) {
                buildCUMSearch(inputValue);
            } else {
                window.location.href = `/search/${inputValue}/`
            }
        });

        // buildCUMSearch()
        async function buildCUMSearch(query = '', page = 1) {
            // console.log("searching:",query)
            cumSearchDiv.style.opacity = '1';
            cumSearchDiv.style.pointerEvents = 'revert';
            document.querySelector('body').style.overflow = 'hidden'
            document.querySelector('html').style.overflow = 'hidden'
            // console.log(cumSearchDiv)


            let searchURL = `${DB_URL}/search.php?q=${query}&p=${page}`;
            // console.log(searchURL)
            let content = await fetchHTML(searchURL);
            // console.log(content)
            content = content.querySelector('#cum_search_results');
            cumSearchDiv.innerHTML = content.outerHTML;

            let box = document.querySelector('#cum_search_results .box');
            box.innerHTML += `<div id="close_cum_search" class="fancybox-item fancybox-close" style="position:absolute;top:-10px;right:-10px;scale:1.25;"></div>`;
            box.style.position = 'relative';
            box.style.overflow = 'visible';

            document.querySelectorAll('a[data-action="cum-search"]').forEach(function (element) {
                element.addEventListener('click', function (event) {
                    event.preventDefault();
                    const page = this.getAttribute('data-page') || 1;
                    const query = this.getAttribute('data-query') || '';
                    buildCUMSearch(query, page);
                    document.getElementById('cum_search_results').scrollIntoView({
                        behavior: 'smooth',
                        block: 'start'
                    });
                });
            });
            document.querySelector('#close_cum_search').addEventListener('click', function (event) {
                closeCUMSearch();
            });

            VM.shortcut.register('right', () => {
                document.querySelector("#list_videos_uploaded_videos_pagination > div > ul > li.next > a")?.click();
            });
            VM.shortcut.register('left', () => {
                document.querySelector("#list_videos_uploaded_videos_pagination > div > ul > li.prev > a")?.click();
            });
            VM.shortcut.register('esc', () => {
                closeCUMSearch();
            });

            let totalVideos = await fetchHTML(`${DB_URL}/scripts/total-videos.php`);
            let totalVideosInt = parseInt(totalVideos.body.textContent);
            let previousTotal = parseInt(localStorage.getItem('previousTotalVideos')) || 0;
            let difference = totalVideosInt - previousTotal;
            localStorage.setItem('previousTotalVideos', totalVideosInt);
            document.querySelector('#cum_search_results').insertAdjacentHTML('beforeend', `<div style="margin-top:.5rem">Serving a total of ${totalVideosInt} indexed videos (+${difference})</div>`);


        }


        function closeCUMSearch() {
            let bg = document.querySelector('#CUM_search_bg');
            if (!bg) return;
            bg.style.opacity = 0;
            bg.style.pointerEvents = 'none';
            document.querySelector('body').style.overflow = 'auto';
            document.querySelector('html').style.overflow = 'auto';


        }












        GM_addStyle(`
.block-video .playlist .pagination .first, .block-video .playlist .pagination .last, .block-video .playlist .pagination .page, .block-video .playlist .pagination .jump{
  display:block}
#CUM_search_bg{
  box-sizing: border-box;
  height: 100lvh;
  width: 100%;
  padding:2rem 5rem 5rem;
  background: hsla(0, 0%, 0%, 20%);
  backdrop-filter: blur(20px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 1s;
  position:absolute;
  top:0;left:0;
  z-index:11;
  overflow-y: auto;
}


    div.search label{
      user-select: none;
      cursor: pointer;
      display: inline-block;
      padding:.25rem;
      margin-left:-.25rem;
      font-size:inherit;
      transition: color .5s;
    }
div.search label input[type="checkbox"]{
      vertical-align: text-top;
      accent-color:#f56c08;
  margin-right:.1rem;

}
div.search label:has(input[type="checkbox"]:not(:checked)){
  color: hsl(36.52deg 10.04% 55.1%);
}
    div.search label:hover,
    div.search label:has(label input[type="checkbox"]:hover),
    div.search label:hover:has(input[type="checkbox"]:not(:checked)){
      color: hsl(36.52deg 50% 80.1%);


    }
`)

    }


    async function autoSearch(query, videoId) {
        return new Promise((resolve) => {
            fetchAndParseHTML(`${baseUrl}/search/${encodeURIComponent(query)}/`, function (htmlDoc) {
                let extractedVideoId = extractLatestVideoId(htmlDoc);

                updateLatestVideo(query, extractedVideoId);
                resolve(extractedVideoId);

            });
        });
    }

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

    async function waitForPageLoad() {
        return new Promise(resolve => {
            if (document.readyState === 'complete') {
                resolve();
            } else {
                window.addEventListener('load', resolve);
            }
        });
    }


    async function processAutoSearches() {
        const autoSearches = retrieveValueFromStorage("auto_search");
        const now = Date.now();
        let newWishlistVideos = loadArrayLocalStorage('newWishlistVideos');

        for (const query in autoSearches) {
            const { timestamp, id } = autoSearches[query];
            if ((now - timestamp) / (1000 * 60) <= backgroundChecksMinutes) continue;

            const newVideoId = await autoSearch(query, id);
            if (newVideoId === id) continue;

            const index = newWishlistVideos.findIndex(video => video.query === query);
            const videoEntry = { query, videoId: newVideoId, timestamp: now };

            if (index !== -1) {
                newWishlistVideos[index] = videoEntry;
            } else {
                newWishlistVideos.push(videoEntry);
            }

            saveArrayLocalStorage('newWishlistVideos', newWishlistVideos);
            if (DEBUG) console.log(`%cNew video found for ${query}`, 'color: #b7e892;');
            lateralAutoSearches();
            newWishedVideo = true;
            newVideoIndicator(newWishedVideo);
            await sleep(1000);
        }
    }



    processAutoSearches();
    lateralAutoSearches();
    newVideoIndicator(newWishedVideo);

    function newVideoIndicator(presence) {
        const container = document.querySelector('.cum_nav .trigger');
        if (!container) return;
        const id = 'new-video-indicator';
        const indicator = document.getElementById(id);
        presence && !indicator ? container.insertAdjacentHTML('beforeend', `<span id="${id}" style="font-size:1.5rem">🆕</span>`) :
            !presence && indicator && indicator.remove();
    }


    function lateralAutoSearches() {
        document.querySelector('.cum_nav')?.remove()
        let newWishlistVideos = loadArrayLocalStorage('newWishlistVideos');
        if (isArrayEmpty(newWishlistVideos)) return;

        let navHtml = `
        <div class="cum_nav">
            <ul class="list-items">
                ${newWishlistVideos.map(video => {
            let text = video.query;
            let truncatedText = text.length > 100 ? text.slice(0, 100) + '…' : text;
            let title = secondsToString(secondsSinceTimestamp(video.timestamp));
            return `<li><a href="/search/${video.query}/" title="${title}">${truncatedText}</a></li>`;
        }).join('')}
              </ul>
            <button title="Click to pin me!" class="trigger">WISH LIST</button>
        </div>`;

        let mostRecentTimestamp = Math.max(...newWishlistVideos.map(video => video.timestamp));
        let autoSearchMenuAge = parseInt(localStorage.getItem('cum_auto_search_menu_age'), 10);
        document.body.insertAdjacentHTML('afterbegin', navHtml);

        if (mostRecentTimestamp > autoSearchMenuAge) {
            newWishedVideo = true;

            newVideoIndicator(newWishedVideo);
        }







        document.querySelector('.trigger').onclick = function () {
            document.querySelector('.cum_nav').classList.toggle('show');
            document.querySelector('.list-items').classList.toggle('show');
            localStorage.setItem('cum_auto_search_menu_age', Date.now());

        };




        GM_addStyle(`
:root{
  --cum-nav-w: 6.5rem;
  --cum-nav-h: 3rem;
  --cum-border-r: .5rem;
}
.trigger {
    color: #ffffff;
    font-family: system-ui, Arial;
    font-weight: 500;
    border: none;
    background: rgba(0, 0, 0, 0.5);
    width: var(--cum-nav-w);
    height: var(--cum-nav-h);
    display: flex;
    cursor: pointer;
    transition: background 0.3s;
    border-radius: var(--cum-border-r) 0 0 var(--cum-border-r);
    flex-direction: row-reverse;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: center;
}
.trigger:hover{
  color: #0BB3E2;
}

.cum_nav {
  opacity:.7;
  box-sizing: border-box;
  list-style: none;
  position: fixed;
  right: 0;
  top: 50%;
  transform: translateY(-50%) ;
  display: flex;
  align-items: center;
  transition: transform 0.3s ease;
}

.list-items {
  display: flex;
  flex-direction: column;
  align-items: center;
  transform: translateX(200%);
  transition: transform 0.3s ease;
  position: relative;
  text-align:center;
  gap:1px;
}
.list-items li a {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  width: 100%;
  text-decoration: none; /* Optional: Remove underline */
  padding: .5rem;
  text-transform: uppercase;
  word-break: break-word;

}
.list-items li {
  background: rgba(0, 0, 0, 0.5);
  width: var(--cum-nav-w);
  height: var(--cum-nav-h);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.3s;
  font-family: system-ui;
    font-weight: 500;

}
.list-items li:first-child {
  border-radius: var(--cum-border-r) 0 0 0;

}
.list-items li:last-child {
  border-radius: 0 0 0 var(--cum-border-r);

}


.cum_nav.show {
  transform: translateY(-50%) translateX(-50%);
  opacity:1;
}

.cum_nav:not(.show):hover {
  transform: translateY(-50%) translateX(-50%);
  opacity:1;
}


`);
    }




    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 ? secondsSinceTimestamp(timestamp) : refreshMinutes * 60 + 1;
    }

    function saveLastVideoID(callback) {
        lastProcessedVideoID(currentID => {
            localStorage.setItem('lastVideoData', JSON.stringify([currentID, Date.now()]));
            callback(loadLastVideoData()[0], currentID);
            if (DEBUG) 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) return

        saveLastVideoID((lastID, newID) => {
            if (newID && lastID && lastID !== newID) {
                alertNewVideo(newID);
            } else {
                if (DEBUG) console.log("No new videos were published");
            }
        });

    }




    advancedOptions["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", "Korea, Republic of": "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", "Russian Federation": "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", "Virgin Islands, British": "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 < 7 * 24 * 60 * 60 * 1000) return;

        await saveValue('lastVersionCheckDate', currentDate.toString());
        const htmlDoc = await fetchHTML(metaPage);
        const metaContent = htmlDoc.body.textContent;
        const metaVersionMatch = metaContent.match(/@version\s+([^\s]+)/);
        if (!metaVersionMatch) return;
        const latestVersion = metaVersionMatch[1];
        // console.log(latestVersion)
        if (latestVersion !== scriptData.version) {
            if (confirm('A new version of the CUM userscript 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");
            }
        }


    }

    advancedOptions['updateReminders'] && updateAlert();



    advancedOptions["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;
        }

        a.avatar{
            overflow:hidden;
        }


    `);




})();