// ==UserScript==
// @name Camwhores.tv Utilities Mod
// @namespace https://sleazyfork.org/users/1281730-vipprograms
// @version 1.14.0
// @description Info preview, removal of lock from friends' vidoes, restored upload button, and more.
// @author vipprograms
// @match https://www.camwhores.tv/*
// @exclude *.camwhores.tv/*mode=async*
// @grant GM_xmlhttpRequest
// @grant GM.getValue
// @grant GM_getValue
// @grant GM.setValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @grant GM.registerMenuCommand
// @grant GM_addStyle
// @grant GM_download
// @grant GM_openInTab
// @grant window.close
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAMUExURQAAAP8ANwwA/////7gbQJkAAAABdFJOUwBA5thmAAAAAWJLR0QDEQxM8gAAAAd0SU1FB+gDHhIuCjXV/h8AAAA4SURBVAjXY2ANDQ1gEA0NDWEIYWBgZAhgAAIUghEiC1YHBhpMDRpIhBbXghUMXKtWLWBgWqHVAACjlwz/pN0YPwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyNC0wMy0zMFQxODo0NjowOSswMDowME+iXNIAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjQtMDMtMzBUMTg6NDY6MDkrMDA6MDA+/+RuAAAAKHRFWHRkYXRlOnRpbWVzdGFtcAAyMDI0LTAzLTMwVDE4OjQ2OjEwKzAwOjAwMNiA/AAAAABJRU5ErkJggg==
// @require https://cdn.jsdelivr.net/npm/@violentmonkey/shortcut@1
// @require https://code.jquery.com/jquery-3.7.1.slim.min.js
// ==/UserScript==
(function () {
'use strict';
let _GM_registerMenuCommand, _GM_notification, options, optionName;
let backgroundChecksMinutes = 60;
const { register } = VM.shortcut;
const currentVersion = GM_info.script.version;
const currentDate = new Date();
const metaPage = "https://update.sleazyfork.org/scripts/491272/Camwhorestv%20Utilities%20Mod.meta.js";
const capitalize = s => (s && s[0].toUpperCase() + s.slice(1)) || "";
const isArrayEmpty = arr => !arr.length;
const processedVideos = new Set();
VM.shortcut.register('c-i', () => {
console.log('You just pressed Ctrl-I');
});
if (typeof GM_registerMenuCommand !== 'undefined') {
_GM_registerMenuCommand = GM_registerMenuCommand;
} else if (typeof GM !== 'undefined' && typeof GM.registerMenuCommand !== 'undefined') {
_GM_registerMenuCommand = GM.registerMenuCommand;
} else {
// _GM_registerMenuCommand = (s, f) => { debug(s); debug(f); };
console.log("Oh no");
}
function saveValue(key, array) {
GM.setValue(key, array).then(function () {
console.log("Array saved successfully.");
}).catch(function (error) {
console.error("Error saving array:", error);
});
}
function saveArraySettings(arrayName, optionName, currentValue) {
let settings = retrieveValueFromStorage(arrayName) || {};
settings[optionName] = currentValue;
saveValue(arrayName, settings);
}
function retrieveValueFromStorage(key) {
if (typeof GM_getValue === "function") {
return GM_getValue(key, false);
}
if (typeof GM === "object" && typeof GM.getValue === "function") {
return GM.getValue(key, false).then(value => value);
}
console.error("Unsupported userscript manager.");
return undefined;
}
function toggleChange(optionName, defaultValue = false, negativeDont = false) { // ONLY for menu commands
let options = retrieveValueFromStorage("options") || {};
let currentState = options[optionName] !== undefined ? options[optionName] : defaultValue;
let commandLabel = negativeDont ? (currentState ? `Don't ${optionName}` : capitalize(optionName))
: (currentState ? `Disable ${optionName}` : `Enable ${optionName}`);
_GM_registerMenuCommand(commandLabel, () => {
options[optionName] = !currentState;
saveValue("options", options);
setTimeout(() => location.reload(), 500);
});
if (options[optionName] === undefined) {
options[optionName] = defaultValue;
saveValue("options", options);
}
}
function createForm(array_name, delimitedString) {
return new Promise((resolve, reject) => {
const form = document.createElement('form');
form.className = 'form-container';
form.innerHTML = `
<textarea class="textarea-input" autofocus>${delimitedString}</textarea>
<div class="buttons-line">
<button type="button" class="cancel-button">Cancel</button>
<button type="submit" class="submit-button">Submit</button>
</div>`;
document.body.appendChild(form);
const removeForm = (message) => {
document.body.removeChild(form);
reject(message);
};
form.querySelector('.cancel-button').onclick = () => removeForm('Form cancelled');
window.onclick = (event) => !form.contains(event.target) && removeForm('Form cancelled');
form.onsubmit = (event) => {
event.preventDefault();
const inputValue = form.querySelector('textarea').value.split('\n').map(line => line.trim()).filter(line => line);
document.body.removeChild(form);
resolve(inputValue);
};
});
}
function optionsArrayEditor(array_name, optionsKeyLegible) {
_GM_registerMenuCommand("Change " + optionsKeyLegible[array_name], () => {
var originalArray = retrieveValueFromStorage(array_name);
var delimitedString = originalArray.join("\n");
createForm(array_name, delimitedString).then(lines => {
saveValue(array_name, lines);
});
});
}
function optionsStringEditor(string_name, optionsKeyLegible) {
_GM_registerMenuCommand(`Change ${optionsKeyLegible[string_name]}`, async () => {
const originalString = retrieveValueFromStorage(string_name);
const lines = await createForm(string_name, originalString);
saveValue(string_name, lines.join('\n'));
});
}
let auto_replies_default = ["Next time you send me a request with 0 videos, I'll block you",
"Very nice videos",
"Why? What's wrong?",
"Sorry, I don't like your videos",
"You don't have any videos"];
let highlight_keywords_default = ['joi', 'cei', 'fuck', "cumshot"];
let friend_request_text_default = "Hi! I'm interested in this video:";
let alternative_thumbnails_users = ['deathstar45', 'usualsuspekt', 'MrPussyGod', 'peacebitch', 'ADCGHN11123', 'Garbage Pile'];
let auto_replies = retrieveValueFromStorage("auto_replies");
let highlight_keywords = retrieveValueFromStorage("highlight_keywords");
let friend_request_text = retrieveValueFromStorage("friend_request_text");
if (!auto_replies) {
auto_replies = auto_replies_default;
saveValue("auto_replies", auto_replies_default);
}
if (!highlight_keywords) {
highlight_keywords = highlight_keywords_default;
saveValue("highlight_keywords", highlight_keywords_default);
}
if (!friend_request_text) {
friend_request_text = friend_request_text_default;
saveValue("friend_request_text", friend_request_text_default);
}
var optionsKeyLegible = {
auto_replies: "template replies",
highlight_keywords: "highlight keywords",
friend_request_text: "friend requests text",
alternative_thumbnails_users: "users with bad default thumbnails"
}
optionsArrayEditor("auto_replies", optionsKeyLegible)
optionsArrayEditor("highlight_keywords", optionsKeyLegible)
optionsStringEditor("friend_request_text", optionsKeyLegible)
optionName = "restore upload button";
toggleChange(optionName, true)
var restoreUploadButton = retrieveValueFromStorage("options")[optionName];
optionName = "notify me of processed videos";
toggleChange(optionName, false)
var notifyProcessedVideos = retrieveValueFromStorage("options")[optionName];
let updateRemindersOptionName = "update reminders";
toggleChange(updateRemindersOptionName, true)
var updateReminders = retrieveValueFromStorage("options")[updateRemindersOptionName];
optionName = "blur images";
toggleChange(optionName, false, true); // name, default, don't as negative
let blurImages = retrieveValueFromStorage("options")[optionName];
const style = document.createElement('style');
// custom CSS
style.textContent = `a.button {color: dimgrey !important;}ul > li.next, ul > li.prev {display: list-item !important;}.item:hover > a > div.img > .friends-tag, .item:hover > a > div.img > .videos-tag {background-color: #1a1a1a !important;}.button {color: rgb(183, 176, 167);text-align: center;border: 1px solid transparent;font-size: 14px;padding: 5px 10px;cursor: pointer;background: linear-gradient(to bottom, #ffffff 0%, #cccccc 100%);border-radius: 3px;display: inline-block;margin: 0 .3rem .3rem 0;}.button:hover {color: #f56c08 !important;background: #1e1e1e;}div.img span.unread-notification {background: #c45606ab;backdrop-filter: blur(5px) brightness(1);top: 0;left: 0;border-bottom-left-radius: 3px;border-bottom-right-radius: 3px;outline: 3px solid #f56c08;animation: glow 5s infinite;}@keyframes glow {0% {outline-color: #f56c08d6;}50% {outline-color: transparent;}100% {outline-color: #f56c08d6;}}.form-container {box-sizing: border-box;position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);max-width: 100vw;width: 800px;padding: 2.5rem;border-radius: .25rem;z-index: 99999;background-color: transparent !important;backdrop-filter: blur(1rem) brightness(.8) contrast(1.2);}.form-container * {box-sizing: inherit;}.textarea-input {width: 100%;min-height: 10rem;padding: 1rem;border-radius: .25rem;color-scheme: dark !important;}.buttons-line {margin-top: .3rem;gap: .3rem;width: 100%;display: flex;}.submit-button, .cancel-button {transform: .3s;}.submit-button {height: 50px;flex-grow: 1;cursor: pointer;border-radius: .25rem;color-scheme: dark !important;font-weight: bold;border: 0;}.cancel-button {width: min-content;height: 50px;cursor: pointer;border-radius: .25rem;color-scheme: dark !important;background-color: red;font-weight: bold;padding-inline: .5rem;border: 0;}input.accept-button, span.accept-button {color: #fff;background-image: linear-gradient(rgb(0, 128, 0) 0%, rgb(0, 255, 0) 100%);}input.accept-button:hover, span.accept-button:hover {color: #fff !important;background-image: linear-gradient(rgb(34, 139, 34) 0%, rgb(50, 205, 50) 100%) !important;}input.reject-button, span.reject-button {color: #fff;background-image: linear-gradient(rgb(255, 0, 0) 0%, rgb(128, 0, 0) 100%);}input.reject-button:hover, span.reject-button:hover {color: #fff !important;background-image: linear-gradient(rgb(220, 20, 60) 0%, rgb(178, 34, 34) 100%) !important;}#vid_previews {display: flex;flex-wrap: nowrap;height: 135px;margin-block: .5rem;gap: 5px;box-sizing: border-box;}#vid_previews .img-div {width: 185px;height: 100%;position: relative;box-sizing: inherit;}.list-messages .added {position: absolute;right: 0;}.list-messages .item {margin-right: 0;}#vid_previews .img-div img {box-sizing: inherit;width: 100%;height: 100%;}#vid_previews .img-div h2 {position: absolute;bottom: 0;background-color: rgba(0, 0, 0, 0.65);font-size: 1rem;line-height: 1.2rem;backdrop-filter: blur(5px);color: rgb(196, 196, 196);width: 100%;word-wrap: break-word;box-sizing: inherit;text-align: center;}.bottom-element {margin-top: 1em;margin-bottom: 1em;}form .bottom {padding-top: .5rem;}form .bottom .submit {float: initial;height: auto;padding: .45rem .5rem;margin: 0;}.margin-fix .bottom {margin: 0;}#gen_joined_text {margin: .5rem 0 .25rem 0;}a[href="http://flowplayer.org"], a.fp-brand {opacity: 0 !important;pointer-events: none !important;}div.block-profile > div > div > div.about-me > div > em {white-space: pre;}h1.online:after {content: '';height: .45rem;aspect-ratio: 1;display: inline-block;background-color: green;position: relative;left: .5rem;border-radius: 50%;bottom: .1rem;}div.block-profile > div > div > div.about-me > div {width: 100%;box-sizing: border-box;}div.block-profile > div > div > div.about-me > div > em {width: 100%;display: inline-block;word-break: auto-phrase;word-wrap: break-word;box-sizing: border-box;text-wrap: pretty;}
.user-search {float: right;position: relative;margin-left: 3px;cursor: pointer;background: linear-gradient(to bottom, #f1f1f1 0%, #d8d8d8 100%);border-radius: 2px;padding: 0;}
.user-search strong {display: block;font-size: 12px;line-height: 15px;padding: 5px 12px 5px 28px;white-space: nowrap;color: #4e4e4e;cursor: pointer;max-width: 200px;overflow: hidden;}
.user-search .type-search {background: url('https://shrph.altervista.org/img/search.png') 5px 4px no-repeat;}
.user-search .icon {display: block;position: absolute;width: 100%;height: 100%;top: 0;}
.user-search:hover {
background: linear-gradient(to bottom, #e1e1e1 0%, #c8c8c8 100%);
}
.user-search:active {
background: linear-gradient(to bottom, #d1d1d1 0%, #b8b8b8 100%);
}
.user-search:hover strong {
color: #333;
}
.user-search:active strong {
color: #111;
}
#tab_screenshots > div > .item, #tab_screenshots > div > .item > .img {width: 250px;height: auto;}
@keyframes fadeAnimation {0% {opacity: 1;}50% {opacity: .3;}100% {opacity: 1;}}.fade {animation: fadeAnimation 1s infinite;}.switch {position: relative;display: inline-block;width: 30px;height: 17px;}.switch input {opacity: 0;width: 0;height: 0;}.slider {position: absolute;cursor: pointer;top: 0;left: 0;right: 0;bottom: 0;background-color: #ccc;transition: .4s;}.slider:before {position: absolute;content: "";height: 13px;width: 13px;left: 2px;bottom: 2px;background-color: white;transition: .4s;}input:checked + .slider {background-color: #f56c08;}input:focus + .slider {box-shadow: 0 0 1px #F28022;}input:checked + .slider:before {transform: translateX(13px);}.slider.round {border-radius: 17px;}.slider.round:before {border-radius: 50%;}.list-comments .dim-comment {background-color: #9e1e1e2b;background-image: none;}.list-comments .dim-comment p {opacity: 1;}
`;
// old:
// style.textContent = `a.button{color:#4e4e4e !important;}ul > li.next, ul > li.prev {display: list-item !important;}.item:hover > a > div.img > .friends-tag, .item:hover > a > div.img > .videos-tag{background-color:#1a1a1a !important;}.button{color: rgb(183, 176, 167);text-align: center;border: 1px solid transparent;font-size: 14px;padding: 5px 10px;cursor: pointer;background: linear-gradient(to bottom, #ffffff 0%, #cccccc 100%);border-radius: 3px;display: inline-block;margin: 0 .3rem .3rem 0;color: dimgrey;}.button:hover{color: #f56c08 !important;border: 1px solid transparent;background: #1e1e1e;}div.img span.unread-notification{background: #c45606ab;backdrop-filter: blur(5px) brightness(1);top: 0px;left: 0px;border-bottom-left-radius: 3px;border-bottom-right-radius: 3px;outline: 3px solid #f56c08;animation: glow 5s infinite;}@keyframes glow {0% {outline-color: #f56c08d6;}50% {outline-color: transparent;}100% {outline-color: #f56c08d6;}}.form-container {box-sizing: border-box;position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);max-width: 100vw;width: 800px;padding: 2.5rem;border-radius: .25rem;z-index:99999;background-color: transparent !important;backdrop-filter: blur(1rem) brightness(.8) contrast(1.2) }.form-container * {box-sizing: inherit;}.textarea-input {width: 100%;min-height: 10rem;padding: 1rem;border-radius: .25rem;color-scheme: dark !important;}.buttons-line {margin-top: .3rem;gap:.3rem;width: 100%;display: flex;}.submit-button:hover, .cancel-button:hover{filter: brightness(.8) contrast(1.2) }.submit-button, .cancel-button{transform: .3s;}.submit-button {height: 50px;flex-grow: 1;cursor: pointer;border-radius: .25rem;color-scheme: dark !important;font-weight:bold;border:0;}.cancel-button {width:min-content;height: 50px;cursor: pointer;border-radius: .25rem;color-scheme: dark !important;background-color: red;font-weight:bold;padding-inline:.5rem;border:0;}input.accept-button, span.accept-button {color: #fff;background-image: linear-gradient(rgb(0, 128, 0) 0%, rgb(0, 255, 0) 100%);}input.accept-button:hover, span.accept-button:hover {color: #fff !important;background-image: linear-gradient(rgb(34, 139, 34) 0%, rgb(50, 205, 50) 100%) !important;}input.reject-button, span.reject-button {color: #fff;background-image: linear-gradient(rgb(255, 0, 0) 0%, rgb(128, 0, 0) 100%);}input.reject-button:hover, span.reject-button:hover {color: #fff !important;background-image: linear-gradient(rgb(220, 20, 60) 0%, rgb(178, 34, 34) 100%) !important;}#vid_previews {display: flex;flex-wrap: nowrap;height: 135px;margin-block: .5rem;gap: 5px;box-sizing: border-box;}#vid_previews .img-div {width:185px;height:100%;position: relative;box-sizing: inherit;}.list-messages .added {position: absolute;right: 0;}.list-messages .item {margin-right: 0;}#vid_previews .img-div img{box-sizing: inherit;width: 100%;height:100%;}#vid_previews .img-div h2{position: absolute;bottom: 0;background-color: rgb(0 0 0 / 65%);font-size: 1rem;line-height: 1.2rem;backdrop-filter: blur(5px);color: rgb(196 196 196);width: 100%;word-wrap: break-word;box-sizing: inherit;text-align: center;}.bottom-element{margin-top: 1em;margin-bottom: 1em;}form .bottom{padding-top: .5rem;}form .bottom .submit{float: initial;height: auto;padding: .45rem .5rem;margin: 0;}.margin-fix .bottom{margin:0;}#gen_joined_text{margin: .5rem 0 .25rem 0;}a[href="http://flowplayer.org"], a[class="fp-brand"]{opacity: 0 !important;pointer-events: none !important;}div.block-profile > div > div > div.about-me > div > em{white-space:pre;}h1.online:after{content: '';height: .45rem;aspect-ratio: 1;display: inline-block;background-color: green;position: relative;left: .5rem;border-radius: 50%;bottom: .1rem;}div.block-profile > div > div > div.about-me > div{width: 100%;box-sizing: border-box;}div.block-profile > div > div > div.about-me > div>em{width: 100%;display: inline-block;word-break: auto-phrase;word-wrap: break-word;box-sizing: border-box;text-wrap: pretty;}.user-search {float: right;position: relative;margin-left: 3px;cursor: pointer;background: #f1f1f1;background: linear-gradient(to bottom, #f1f1f1 0%, #d8d8d8 100%);border-radius: 2px;padding:0;}.user-search strong {display: block;font-size: 12px;line-height: 15px;padding: 5px 12px 5px 28px;white-space: nowrap;color: #4e4e4e;cursor: pointer;max-width: 200px;overflow: hidden;}.user-search .type-search {background: url('https://shrph.altervista.org/img/search.png') 5px 4px no-repeat;}.user-search .icon {display: block;position: absolute;width: 100%;height: 100%;top:0;}#tab_screenshots > div > .item, #tab_screenshots > div > .item > .img {width: 250px;height: auto;}#tab_screenshots > div > .item > .img {}@keyframes fadeAnimation {0% {opacity: 1;}50% {opacity: .3;}100% {opacity: 1;}}.fade {animation: fadeAnimation 1s infinite;}.switch {position: relative;display: inline-block;width: 30px;height: 17px;}.switch input {opacity: 0;width: 0;height: 0;}.slider {position: absolute;cursor: pointer;top: 0;left: 0;right: 0;bottom: 0;background-color: #ccc;-webkit-transition: .4s;transition: .4s;}.slider:before {position: absolute;content: "";height: 13px;width: 13px;left: 2px;bottom: 2px;background-color: white;-webkit-transition: .4s;transition: .4s;}input:checked + .slider {background-color: #f56c08;}input:focus + .slider {box-shadow: 0 0 1px #F28022;}input:checked + .slider:before {-webkit-transform: translateX(13px);transform: translateX(13px);}.slider.round {border-radius: 17px;}.slider.round:before {border-radius: 50%;}.list-comments .dim-comment p {opacity: 1;}.list-comments .dim-comment {background-color: #9e1e1e2b;background-image: none;}
// `;
document.head.appendChild(style);
function isNotPage(...pages) {
const currentUrl = window.location.href.split(/[?#]/)[0];
return !pages.includes(currentUrl);
}
function makeValidUrlString(inputString) {
var validString = inputString.replace(/[^\w\s\/-]/g, ''); // Remove special characters
validString = validString.replace(/[-_]{2,}/g, '-'); // Turn consecutive underscores or hyphens into single hyphens
validString = validString.replace(/\s+/g, '-'); // Turn spaces into hyphens
validString = validString.replace(/\//g, '-'); // Turn slashes into hyphens
return validString;
}
function friendRequestSetup(video_title, video_url, userID) {
var vid_code = video_url.replace("https://www.camwhores.tv/videos/", "");
var parts = vid_code.split("/");
var videoId = parts[0];
var code = parts[1];
let cumData = {
title: video_title,
videoId: videoId,
userId: userID
};
let lastCumVideoAdd = JSON.parse(localStorage.getItem('last_cum_video_data')) || {};
lastCumVideoAdd[userID] = cumData;
localStorage.setItem('last_cum_video_data', JSON.stringify(lastCumVideoAdd));
}
function convertToSeconds(timeString) {
const [_, value, unit] = timeString.match(/(\d+) (\w+)/);
const conversion = {
second: 1,
seconds: 1,
minute: 60,
minutes: 60,
hour: 3600,
hours: 3600,
day: 86400,
days: 86400,
week: 604800,
weeks: 604800,
month: 2592000, // 30 days
months: 2592000, // 30 days
year: 31536000, // 365 days
years: 31536000 // 365 days
};
return value * (conversion[unit] || 0);
}
function fetchAndParseHTML(url, callback) {
GM_xmlhttpRequest({
method: "GET",
url: url,
onload: function (response) {
var parser = new DOMParser();
var htmlDoc = parser.parseFromString(response.responseText, "text/html");
callback(htmlDoc);
}
});
}
function fetchAndExtract(url, callback) {
GM_xmlhttpRequest({
method: "GET",
url: url,
onload: function (response) {
document.evaluate('//*[@id="list_messages_my_conversation_messages_items"]/div/div[3]/img', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue?.remove();
const htmlDoc = new DOMParser().parseFromString(response.responseText, "text/html");
callback(htmlDoc);
}
});
}
function getAllSessionStorage() {
const sessionStorageItems = {};
for (let i = 0; i < sessionStorage.length; i++) {
const key = sessionStorage.key(i);
sessionStorageItems[key] = sessionStorage.getItem(key);
}
return sessionStorageItems;
}
function getCurrentPage() {
const allSessionStorage = getAllSessionStorage();
const paramsStorageKey = Object.keys(allSessionStorage).find(key => key.endsWith(":params"));
if (!paramsStorageKey) return 1;
const paramsValue = JSON.parse(allSessionStorage[paramsStorageKey])?.from_my_conversations;
return parseInt(paramsValue, 10) || 1;
}
function setUploadButtonTimestamp() {
localStorage.setItem('UploadButtonTimestamp', JSON.stringify(Date.now()));
}
function getCookie(name) {
let cookieArr = document.cookie.split("; ");
for (let cookie of cookieArr) {
let [key, value] = cookie.split("=");
if (key === name) return value;
}
return null;
}
function loadArrayLocalStorage(name = 'lastVideoData') {
var jsonData = localStorage.getItem(name);
if (!jsonData) {
return [];
}
var data = JSON.parse(jsonData);
return data;
}
function saveArrayLocalStorage(name='lastVideoData',data) {
var timestamp = Date.now();
var jsonData = JSON.stringify(data);
localStorage.setItem(name, jsonData);
}
function secondsSinceLocalStorage(name) {
const storedItem = JSON.parse(localStorage.getItem(name));
return storedItem ? (Date.now() - storedItem.timestamp) / 1000 : null;
}
function getNewUID() {
const sessionStorageData = getAllSessionStorage();
if (!sessionStorageData) return null;
for (const key in sessionStorageData) {
const match = key.match(/^(\d+):https:\/\/www\.camwhores\.tv/);
if (match) return match[1];
}
return null;
}
function saveUID() {
const uid = getNewUID();
if (uid) {
const current_kt = getCookie("kt_member");
saveArrayLocalStorage("cum_user_id", { uid, kt: current_kt });
return uid;
}
return null;
}
function silentUidUpdater() {
const uidArray = loadArrayLocalStorage("cum_user_id");
if (!uidArray || getCookie("kt_member") !== uidArray.kt) {
saveUID();
}
}
function currentUserID() {
const current_kt = getCookie("kt_member");
const uidArray = loadArrayLocalStorage("cum_user_id");
return uidArray && uidArray.kt === current_kt ? uidArray.uid : saveUID();
}
async function getResponseStatus(url) {
try {
const response = await fetch(url, { method: 'HEAD' });
return response.status;
} catch {
return null;
}
}
function secondsSinceTimestamp(timestamp) {
return (Date.now() - timestamp) / 1000;
}
const currentUID = currentUserID();
// console.log("User ID:", currentUID, "(https://www.camwhores.tv/members/" + currentUID + "/)")
silentUidUpdater();
function uploadRestored() {
alert("The upload button has been restored!");
localStorage.removeItem("UploadButtonTimestamp");
}
async function handleUploadButton() {
const nav = document.querySelector('.navigation .primary');
const lastCheck = localStorage.getItem("UploadButtonTimestamp");
const minutesSinceLastCheck = lastCheck ? secondsSinceTimestamp(lastCheck) / 60 : null;
if (nav && restoreUploadButton && ![...nav.querySelectorAll('a')].some(link => link.textContent.trim() === 'Upload')) {
const uploadLink = document.createElement('a');
uploadLink.style.cssText = 'background-color: #215521; display: block; padding: 11px 0; cursor: pointer;';
uploadLink.textContent = 'UPLOAD';
uploadLink.href = '/upload-video/';
nav.appendChild(uploadLink);
uploadLink.addEventListener('click', async (event) => {
event.preventDefault();
const status = await getResponseStatus('/upload-video/');
if (status === 200) {
window.location.href = '/upload-video/';
} else if (confirm(`Upload functionality is disabled (${status}). Notify me when available?`)) {
setUploadButtonTimestamp();
}
});
if (lastCheck && minutesSinceLastCheck > 30) {
const status = await getResponseStatus('/upload-video/');
status === 200 ? uploadRestored() : (console.log(`Upload button still unavailable. Last check ${Math.floor(minutesSinceLastCheck)} minutes ago`), setUploadButtonTimestamp());
}
} else if (lastCheck) {
uploadRestored();
}
}
handleUploadButton();
if (window.location.href.endsWith("?action=reject_add_to_friends_done") ||
window.location.href.endsWith("?action=confirm_add_to_friends_done")) {
const urlOfUser = document.querySelector('#list_messages_my_conversation_messages > div.headline > h2 > a:nth-child(2)');
const username = urlOfUser.innerHTML.trim();
const userID = urlOfUser.getAttribute('href').match(/\/(\d+)\//)[1];
const storedData = JSON.parse(localStorage.getItem('closedWindow'));
if (storedData) {
const { uid, timestamp } = storedData;
const timeDifference = (Date.now() - timestamp) / 1000; // in seconds
if (timeDifference < 30 && userID === uid) {
console.log(`Less than 30 seconds have passed since window closed. User ID: ${uid}`);
window.close();
}
}
}
if (window.location.href == "https://www.camwhores.tv/my/messages/") {
document.addEventListener('visibilitychange', function() {
if (document.visibilityState === 'visible') {
window.location.reload();
}
});
function myMessagesAsyncURL(pageInt = 1) {
const utcTimestamp = new Date().getTime();
const pageID = pageInt.toString();
return "https://www.camwhores.tv/my/messages/?mode=async&function=get_block&block_id=list_members_my_conversations&sort_by=added_date&from_my_conversations=" + pageID + "&_=" + utcTimestamp;
}
// console.log(myMessagesAsyncURL(3))
function messagePageInfo(pageN) {
let urlToPage = myMessagesAsyncURL(pageN);
// console.log(urlToPage)
return new Promise((resolve, reject) => {
fetchAndExtract(urlToPage, function (htmlDoc) {
const conversations = [];
const items = htmlDoc.querySelectorAll("#list_members_my_conversations_items .item");
// console.log("htmlDoc:",htmlDoc)
items.forEach(item => {
const name = item.querySelector('a').getAttribute('title');
const unread = item.classList.contains('unread');
conversations.push({ name, unread });
});
resolve(conversations);
});
});
}
async function hasUnreadMessage(currentPage) {
const data = await messagePageInfo(currentPage);
return data.some(message => message.unread);
}
async function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function lastUnreadPage() {
let currentPage = 1;
let hasUnread = true;
let lastUnreadPage = 0;
let pagesChecked = 0;
while (pagesChecked < 10 || hasUnread) {
const data = await messagePageInfo(currentPage);
hasUnread = data.some(message => message.unread);
if (hasUnread) {
lastUnreadPage = currentPage;
}
pagesChecked++;
currentPage++;
await delay(75);
}
return lastUnreadPage;
}
async function checkAndFixMessages() {
const currentPage = getCurrentPage();
const data = await messagePageInfo(currentPage);
// console.log(data);
let hasNewElements = false;
data.forEach(item => {
const userItem = document.querySelector(`#list_members_my_conversations_items > div.item > a[title="${item.name}"]`);
if (userItem) {
const unreadNotification = userItem.querySelector('div.img .unread-notification');
if (unreadNotification && !item.unread) {
unreadNotification.remove();
console.log("Message with", item.name, "was read");
}
} else {
hasNewElements = true;
}
});
if (hasNewElements) {
location.reload();
}
}
function processThumbnails() {
// console.log("Processing thumbnails");
var thumbnails = document.querySelectorAll('strong.title');
if (thumbnails.length === 0) {
console.log("Thumbnails not found. Retrying in 1000 milliseconds.");
setTimeout(function () {
processThumbnails(); // Retry processing thumbnails
}, 1000);
return false; // Return false if thumbnails not found
}
thumbnails.forEach(function (thumbnail) {
var usernameText = thumbnail.textContent.trim();
if (usernameText) {
var usernameLink = document.querySelector('a[title="' + usernameText + '"]');
// console.log("usernameLink:",usernameLink)
if (usernameLink) {
var href = usernameLink.getAttribute('href');
var userIdMatch = href.match(/\/(\d+)\/$/);
if (userIdMatch && userIdMatch.length > 1) {
var userId = userIdMatch[1];
var usernameUrl = "https://www.camwhores.tv/members/" + userId + "/";
GM_xmlhttpRequest({
method: "GET",
url: usernameUrl,
onload: function (response) {
var parser = new DOMParser();
var htmlDoc = parser.parseFromString(response.responseText, "text/html");
var infoMessage = htmlDoc.querySelector('.info-message');
var usernameLink = document.querySelector('a[title="' + usernameText + '"]');
// console.log(usernameText + ": " + usernameUrl)
// console.log(usernameLink)
if (infoMessage && infoMessage.textContent.includes("is in your friends list.")) {
if (usernameLink) {
var divElement = document.createElement('div');
divElement.classList.add("friends-tag");
divElement.textContent = "Friends ✅";
divElement.style.position = "absolute";
divElement.style.top = "0";
divElement.style.right = "0";
divElement.style.backgroundColor = "#414141"; //#1a1a1a
divElement.style.borderBottomLeftRadius = "3px";
divElement.style.color = "white";
divElement.style.padding = "3px 5px";
if (usernameLink) {
var firstDivElement = usernameLink.querySelector('div.img');
if (firstDivElement) {
firstDivElement.appendChild(divElement);
}
}
}
}
if (infoMessage && infoMessage.textContent.includes("wants to be your friend.")) {
divElement = document.createElement('div');
divElement.textContent = "👀";
divElement.title = "Wants to be your friend!";
divElement.style.position = "absolute";
divElement.style.bottom = "0";
divElement.style.left = "0";
// divElement.style.backgroundColor = "aquamarine";
divElement.style.color = "white";
divElement.style.fontSize = "1rem";
divElement.style.padding = ".5rem .25rem";
if (usernameLink) {
firstDivElement = usernameLink.querySelector('div.img');
if (firstDivElement) {
firstDivElement.appendChild(divElement);
}
}
}
var titleVideosElement = htmlDoc.evaluate('//*[@id="list_videos_uploaded_videos"]/div[1]/h2', htmlDoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
var videoNumber = 0;
if (titleVideosElement && titleVideosElement.singleNodeValue) {
var titleVideosText = titleVideosElement.singleNodeValue.textContent;
if (titleVideosText) {
var matches = titleVideosText.match(/\(([^)]+)\)/);
videoNumber = matches ? matches[1] : 0;
}
}
if (videoNumber || videoNumber === 0) {
divElement = document.createElement('div');
divElement.classList.add("videos-tag");
divElement.textContent = videoNumber;
divElement.style.position = "absolute";
divElement.style.bottom = "0";
divElement.style.right = "0";
divElement.style.backgroundColor = "CadetBlue";
divElement.style.color = "white";
divElement.style.padding = "3px 5px";
divElement.style.borderTopLeftRadius = "3px";
}
if (usernameLink) {
firstDivElement = usernameLink.querySelector('div.img');
if (firstDivElement) {
firstDivElement.appendChild(divElement);
}
}
} // end httpGET reponse
});
} else {
console.log("User ID not found in href for:", usernameText);
}
} else {
console.log("Username link not found for:", usernameText);
}
}
});
return true; // Return true if thumbnails processed successfully
}
function addButtonEventListener() {
// console.log("Adding event listener to buttons");
document.addEventListener('click', function (event) {
var target = event.target;
if (target && target.matches('a[data-action="ajax"]')) {
// console.log("Button clicked");
var h2Element = document.querySelector('#list_members_my_conversations div.headline h2');
const initialText = h2Element.textContent.trim();
// console.log("Initial text: " + initialText);
const checkChangeInterval = setInterval(function () {
h2Element = document.querySelector('#list_members_my_conversations div.headline h2');
const currentText = h2Element.textContent.trim();
// console.log("Current text: " + currentText);
if (currentText !== initialText) {
clearInterval(checkChangeInterval);
processThumbnails();
}
}, 100);
}
});
}
setTimeout(function () {
processThumbnails();
var thumbnailsProcessed = processThumbnails();
if (!thumbnailsProcessed) {
console.log("Processing thumbnails failed. Retrying in 1000 milliseconds.");
setTimeout(function () {
processThumbnails(); // Retry processing thumbnails
}, 1000);
}
addButtonEventListener();
}, 1000);
setInterval(function () {
processThumbnails()
silentUidUpdater()
checkAndFixMessages()
}, 5000);
setInterval(function () {
addButtons()
}, 500);
function addButtons() {
const h2Element = document.querySelector('#list_members_my_conversations .headline h2');
if (!h2Element) return console.log('h2Element not found');
if (!document.getElementById('refreshIcon')) {
const refreshIcon = document.createElement('span');
refreshIcon.id = 'refreshIcon';
refreshIcon.innerHTML = '↻';
refreshIcon.title = 'Refresh Utilities Mod';
refreshIcon.style = 'cursor:pointer;margin-left:10px;';
refreshIcon.addEventListener('click', () => {
processThumbnails();
console.log('Refreshing...');
});
h2Element.appendChild(refreshIcon);
}
if (!document.getElementById('lastUnreadButton')) {
const lastUnreadButton = document.createElement('a');
lastUnreadButton.id = 'lastUnreadButton';
lastUnreadButton.innerHTML = '⏭';
lastUnreadButton.title = 'Go to oldest unread message';
lastUnreadButton.style = 'cursor:pointer;margin-left:10px;text-decoration:none;';
lastUnreadButton.addEventListener('click', async () => {
lastUnreadButton.classList.add('fade');
const oldestUnreadPage = await lastUnreadPage();
console.log(oldestUnreadPage);
const linkElement = document.querySelector(`a[href="/my/messages/${oldestUnreadPage}/"][data-block-id="list_members_my_conversations"]`);
if (linkElement) {
linkElement.click();
} else {
console.log(`Element for page ${oldestUnreadPage} not found`);
}
lastUnreadButton.classList.remove('fade');
});
h2Element.appendChild(lastUnreadButton);
}
}
addButtons()
}
if (window.location.href.startsWith("https://www.camwhores.tv/my/messages/") && window.location.href !== "https://www.camwhores.tv/my/messages/") {
function updateButtonStyle(button, state) {
button?.classList.add(state ? "accept-button" : "reject-button");
}
let textarea = document.querySelector("#send_message_message");
let divTextarea = document.createElement('div');
let urlOfUser = document.querySelector('#list_messages_my_conversation_messages > div.headline > h2 > a:nth-child(2)')
let username = urlOfUser.innerHTML.trim();
let userID = urlOfUser.getAttribute('href').match(/\/(\d+)\//)[1];
let confirmButton = document.querySelector('input[name="confirm"]');
let rejectButton = document.querySelector('input[name="reject"]');
let buttonsParent = confirmButton ? confirmButton.parentElement : null;
let confirmClose = null
let denyClose = null
if (sessionStorage.getItem('continue') === 'true') {
sessionStorage.removeItem('continue');
if (history.length === 1) window.location.href = `https://www.camwhores.tv/members/${userID}/#list_videos_my_uploaded_videos`;
}
confirmButton?.addEventListener('click', () => sessionStorage.setItem('continue', 'true'));
document.querySelector('input.submit')?.addEventListener('click', () => divTextarea.innerHTML = '');
$(document).ready(() => {
$(document).keyup(event => {
if (event.ctrlKey && event.key === 'Enter') {
event.preventDefault();
$("#send_message_form").submit();
}
});
});
function copyContentToTextarea() {
const content = Array.from(divTextarea.childNodes).map(node =>
node.nodeType === 3 ? node.textContent : node.tagName === 'IMG' ? node.alt : ''
).join('').trim();
textarea.value = content;
}
textarea.style.display = "none";
Object.assign(divTextarea.style, {
display: "block",
minHeight: "5rem",
resize: "vertical",
overflow: "auto",
fontSize: "14px"
});
divTextarea.classList.add("textarea");
divTextarea.contentEditable = true;
textarea.parentNode.insertBefore(divTextarea, textarea.nextSibling);
document.querySelectorAll('.smileys-bar img').forEach(img =>
img.onclick = e => {
e.preventDefault();
e.stopPropagation();
divTextarea.appendChild(Object.assign(img.cloneNode(true), { style: { cursor: "auto" } }));
copyContentToTextarea();
}
);
divTextarea.oninput = copyContentToTextarea;
document.querySelectorAll('span.inline-text, span.original-text').forEach(function(span) {
var lineText = span.textContent.trim();
if (lineText.includes('##')) {
var index = lineText.indexOf(':');
var videoText = lineText.substring(0, index).trim();
var videoInfo = lineText.substring(index + 1).trim().replace('##', '');
let parts = videoInfo.split("\n");
let videoCode = parts[0];
let videoID = parts[1];
let url = 'https://www.camwhores.tv/videos/' + videoID + "/" + makeValidUrlString(videoCode) + '/';
// console.log(url)
fetch(url)
.then(response => {
if (response.ok) {
span.innerHTML = videoText + `<a href="` + url + '" class="toggle-button" style="display:inline-block">'+videoCode+'</a>';
} else if (response.status === 404) {
console.log("URL is 404");
} else {
console.error("Error:", response.status);
}
})
.catch(error => console.error("Error:", error));
// span.innerHTML = videoText + ': <a href="' + url + '" class="toggle-button" style="display:inline-block">Open video</a>';
}
});
function andClosePage() {
localStorage.setItem('closedWindow', JSON.stringify({ uid: userID, timestamp: Date.now() }));
window.close();
}
function addCloseButtonsListeners(confirmButton, rejectButton) {
if (!confirmButton) return {};
const createButton = (text, action) => {
const btn = document.createElement('span');
btn.className = 'submit button';
btn.textContent = text;
btn.addEventListener('click', event => {
event.preventDefault();
action();
andClosePage();
});
return btn;
};
const buttonsParent = confirmButton.parentElement;
const confirmClose = createButton('Confirm & Close', () => confirmButton.click());
const denyClose = createButton('Deny & Close', () => rejectButton.click());
buttonsParent.append(confirmClose, ' ', denyClose);
confirmButton.style.width = `${confirmClose.offsetWidth}px`;
rejectButton.style.width = `${denyClose.offsetWidth}px`;
return { confirmClose, denyClose };
}
function updateMessageDOM(htmlDoc, confirmClose, denyClose) {
const twoYearsSeconds = 60 * 60 * 24 * 365 * 2;
var titleText = htmlDoc.querySelector('#list_videos_uploaded_videos > div:nth-child(1) > h2')?.textContent.trim();
var lastUploadTime = htmlDoc.querySelector('#list_videos_uploaded_videos_items > div.item > a > div.wrap > div.added > em')?.textContent.trim();
var joinedDateText = htmlDoc.querySelector('div.block-profile > div.profile-list > div.margin-fix > div.column > div:nth-child(3) > em')?.textContent.trim();
var lastLogin = htmlDoc.querySelector('div.block-profile > div.profile-list > div.margin-fix > div.last-login > div.item')?.textContent.trim();
let secondsSinceOnline = convertToSeconds(lastLogin);
let recentlyOnline = secondsSinceOnline <= 300;
// let recentlyOnline = true;
const headlineH2 = document.querySelector('#list_messages_my_conversation_messages .headline h2');
console.log("recentlyOnline:", recentlyOnline);
var recentlyUploaded = false;
console.log("Has focus:",document.hasFocus(),"titleText:",titleText)
if(!document.hasFocus() && !titleText){
denyClose.click()
window.close();
}
if(lastUploadTime){
let lastUploadTimeSeconds = convertToSeconds(lastUploadTime);
recentlyUploaded = lastUploadTimeSeconds < twoYearsSeconds;
}
if(recentlyOnline){
GM_addStyle(`.list-messages .item.new .added:after {
background-color: green;
}`);
document.querySelector("#list_messages_my_conversation_messages_items > div.item.new > div.added").textContent += " (online)";
headlineH2.innerHTML += " (online)"
}
if (titleText) { // THERE ARE VIDEOS
var extractedTitle = titleText.match(/\(([^)]+)\)/);
if (extractedTitle && extractedTitle.length >= 1) {
// THERE ARE VIDEOS
if(recentlyUploaded){
updateButtonStyle(confirmButton, true);
updateButtonStyle(confirmClose, true);
}else{
updateButtonStyle(rejectButton, false);
updateButtonStyle(denyClose, false);
}
var generatedTextDiv = document.createElement('div');
generatedTextDiv.id = 'gen_joined_text';
generatedTextDiv.innerHTML = `${extractedTitle[1]} videos, last ${lastUploadTime}<br>Joined ${joinedDateText}`;
var messageDiv = document.querySelector('#list_messages_my_conversation_messages_items > div > div:nth-child(3)');
if (messageDiv) {
messageDiv.appendChild(generatedTextDiv);
}
}else{
if(extractedTitle) console.log("extractedTitle:",extractedTitle)
}
if(confirmClose){
let imgData = [];
for (let i = 1; i <= 5; i++) {
let xpath = `//*[@id="list_videos_uploaded_videos_items"]/div[${i}]/a/div[@class="img"]/img`;
let img = htmlDoc.evaluate(xpath, htmlDoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
if (img) {
let parentAnchor = img.parentNode.parentNode;
imgData.push({ src: img.getAttribute('data-original'), alt: img.alt, url: parentAnchor.getAttribute('href') });
}
}
let vidPreviewsDiv = document.createElement("div");
vidPreviewsDiv.id = "vid_previews";
if (!isArrayEmpty(imgData)) {
imgData.forEach(data => {
let imgDiv = document.createElement("div");
imgDiv.classList.add("img-div");
let imgLink = document.createElement("a"); // New line
imgLink.href = data.url+"?please_check_friend"; // New line
let img = document.createElement("img");
img.src = data.src;
img.alt = data.alt;
img.title = data.alt;
let title = document.createElement("h2");
title.textContent = data.alt;
imgLink.appendChild(img); // Updated line
imgDiv.appendChild(imgLink); // Updated line
imgDiv.appendChild(title);
vidPreviewsDiv.appendChild(imgDiv);
});
buttonsParent.appendChild(vidPreviewsDiv);
}
}
} else {
// THERE ARE NO VIDEOS
console.log('No videos? Title: '+titleText+", content: "+lastUploadTime);
updateButtonStyle(rejectButton, false);
updateButtonStyle(denyClose, false);
var messageDiv = document.querySelector('#list_messages_my_conversation_messages_items > div > div:nth-child(3)');
if (messageDiv) {
var generatedTextDiv = document.createElement('div');
generatedTextDiv.id = 'gen_joined_text';
generatedTextDiv.innerHTML = `Joined ${joinedDateText}<br><b>No videos</b>`;
messageDiv.appendChild(generatedTextDiv);
}
}
}
const bottomDiv = document.querySelector("#send_message_form > div > div.bottom");
const responsesDiv = document.createElement("div");
responsesDiv.id = "responses";
responsesDiv.style.marginTop = "1rem";
bottomDiv.insertAdjacentElement('afterend', responsesDiv);
auto_replies.forEach(string => {
const button = document.createElement("button");
button.textContent = string;
button.className = "button";
button.style.padding = ".25rem .5rem";
button.addEventListener("click", e => {
e.preventDefault();
textarea.value += textarea.value.trim() ? `\n${string}` : string;
document.querySelector('#send_message_form').submit();
});
responsesDiv.appendChild(button);
});
var messageLink = document.evaluate('//*[@id="list_messages_my_conversation_messages_items"]/div/div[1]/a', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
if (messageLink && messageLink.singleNodeValue) {
var messageUrl = messageLink.singleNodeValue.href;
const parentElement = document.evaluate('//*[@id="list_messages_my_conversation_messages_items"]/div/div[3]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
const img = document.createElement('img');
img.src = 'https://samherbert.net/svg-loaders/svg-loaders/three-dots.svg'; // loading spinner
img.style.height = "32px";
img.style.width = "32px";
parentElement.appendChild(img);
fetchAndExtract(messageUrl, function(htmlDoc) {
var closeButtons = addCloseButtonsListeners(confirmButton, rejectButton);
var confirmClose = closeButtons.confirmClose;
var denyClose = closeButtons.denyClose;
updateMessageDOM(htmlDoc, confirmClose, denyClose);
});
} else {
console.log('Message link not found.');
}
// AESTHETIC CHANGE
var bottomElement = document.evaluate('//*[@id="list_messages_my_conversation_messages_items"]/div/div[3]/form/div', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
if (bottomElement && bottomElement.singleNodeValue) {
bottomElement.classList.add('bottom-element');
bottomElement.singleNodeValue.style.marginTop = "1em";
bottomElement.singleNodeValue.style.marginBottom = "1em";
bottomElement.singleNodeValue.style.outline = "2px solid red";
bottomElement.singleNodeValue.classList.remove("bottom");
}
}
if (isNotPage("https://www.camwhores.tv/my/videos/", "https://www.camwhores.tv/my/")) {
function addPrivateVideos() {
const private_videos = [];
const videoElements = document.querySelectorAll('.line-premium, .line-private');
videoElements.forEach(video => {
const parentDiv = video.parentElement;
if (
parentDiv.tagName === 'DIV' &&
parentDiv.parentElement.tagName === 'A' &&
!processedVideos.has(parentDiv.parentElement)
) {
private_videos.push(parentDiv.parentElement);
processedVideos.add(parentDiv.parentElement);
}
});
return private_videos;
}
async function checkVideoPage(url) {
try {
const response = await fetch(url);
if (!response.ok) {
console.error('Error loading video page:', response.status);
return { byFriend: false, usernameID: null };
}
const html = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const hasElement1 = doc.querySelector('body > div.container > div.content > div.block-video > div.video-holder > div.player > div > div > span') !== null;
const hasElement2 = doc.querySelector('body > div.container > div.content > div.block-album > div.album-holder > div.images > span.message') !== null;
let username = doc.querySelector("#tab_video_info > div > div.block-user > div.username > a") || doc.querySelector("#tab_album_info > div > div.block-user > div > a")?.textContent.trim();
var vid_code = doc.querySelector('.username a').href.replace("https://www.camwhores.tv/members/", "");
var usernameID = vid_code.split("/")[0];
return { byFriend: hasElement1 || hasElement2, usernameID: usernameID, username: username };
} catch (error) {
console.error('Error checking video page:', error);
return { byFriend: false, usernameID: null };
}
}
function updateImageSrc(image) {
const src = image?.src;
if (src?.endsWith('1.jpg')) image.src = src.replace('1.jpg', '5.jpg');
}
async function applyStyleToPrivateVideos() {
for (const video of addPrivateVideos()) {
const img = video.querySelector('div > img');
const link = video.href;
if(!link) continue;
const { byFriend, usernameID, username } = await checkVideoPage(link);
if (!byFriend) {
video.style.outline = 'green solid 2px';
let linePrivate = video.querySelector('.line-private');
if (linePrivate) linePrivate.style.display = 'none';
if (img) img.style.opacity = '1';
} else {
const linePrivate = video.querySelector('.line-private > span');
if (linePrivate) {
linePrivate.textContent += ' ❌';
const addFriend = document.createElement('a');
Object.assign(addFriend, {
textContent: '➕',
title: 'Friend request',
href: `https://www.camwhores.tv/members/${usernameID}/#friends`,
style: 'z-index: 10; position: absolute; bottom: 20px; right: 0; font-size: 1rem; padding: 5px 2.5px; background-color: hsla(0, 0%, 0%, 0.7);'
});
addFriend.addEventListener('mousedown', e => {
e.stopImmediatePropagation();
friendRequestSetup(video.querySelector('strong.title').textContent.trim(), link, usernameID);
// console.log("Added video to data")
});
video.querySelector('.img').appendChild(addFriend);
}
}
if (alternative_thumbnails_users.includes(username)) updateImageSrc(img);
await new Promise(resolve => setTimeout(resolve, 25)); // avoid 503
}
}
function highlightKeywords() {
document.querySelectorAll('.title').forEach(title => {
highlight_keywords.forEach(keyword => {
title.innerHTML = title.innerHTML.replace(new RegExp(keyword, 'gi'), '<span style="color: green;">$&</span>');
});
});
}
applyStyleToPrivateVideos();
highlightKeywords();
setInterval(() => {
applyStyleToPrivateVideos();
highlightKeywords();
}, 500);
}
if (window.location.href.startsWith("https://www.camwhores.tv/members/")) {
const userId = window.location.href.split('/').slice(-2, -1)[0];
const userVideosH2 = document.querySelector('#list_videos_uploaded_videos .headline h2');
const userDescription = document.querySelector("div.block-profile > div.profile-list > div > div.about-me > div > em");
const lastLogin = document.querySelector('div.block-profile > div.profile-list > div.margin-fix > div.last-login > div.item')?.textContent.trim();
const h1 = document.querySelector("h1");
const country = document.querySelector('div.block-profile > div.profile-list > div.margin-fix > div.column > div:nth-child(1) > em');
const countryName = country.textContent.trim();
const countryCode = contryCodesArray()[countryName] || [];
const parent = document.querySelector('div.block-profile > div.profile-list > div.margin-fix > div.column > div:nth-child(1)');
const recentlyOnline = convertToSeconds(lastLogin) <= 300;
function userProfileAsyncURL(uid, page) {
return `https://www.camwhores.tv/members/${uid}/?mode=async&function=get_block&block_id=list_videos_uploaded_videos&sort_by=&from_videos=${page}&_=${Date.now()}`;
}
function numberBetweenParenthesis(text) {
return text?.match(/\((\d+)\)/)?.[1] ?? null;
}
function getRoundedTotalPages(h2) {
const total = numberBetweenParenthesis(h2?.textContent);
return total ? Math.ceil(total / 5) : null;
}
function updateVideoH2() {
const roundedPages = getRoundedTotalPages(userVideosH2);
if (roundedPages) {
userVideosH2.innerHTML = userVideosH2.innerHTML.includes("Page") ? userVideosH2.innerHTML : userVideosH2.innerHTML.trim() + `/${roundedPages}`;
} else {
console.log("No rounded pages.");
}
}
function addSearchButton() {
if(document.querySelector('#cum_vid_search')) return;
let sortElement = document.querySelector("#list_videos_uploaded_videos > div.headline > div.sort");
if (sortElement) {
const searchButton = document.createElement('div');
searchButton.className = 'user-search';
searchButton.innerHTML = '<strong>Search videos</strong><span class="icon type-search"></span>';
searchButton.id = 'cum_vid_search';
searchButton.addEventListener('click', searchDialog);
sortElement.parentNode.insertBefore(searchButton, sortElement);
}
}
function searchDialog() {
if (document.querySelector('#search-dialog')) return;
console.log("Creating dialog...");
let dialog = document.createElement('div');
dialog.id = "search-dialog";
dialog.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%, -50%);backdrop-filter:blur(5px);background:rgba(0,0,0,0.44);padding:20px;border-radius:10px;box-shadow:0 4px 8px rgba(0,0,0,0.1);z-index:1000;';
let searchHighlights = retrieveValueFromStorage("highlights_search") || [];
dialog.innerHTML = `
<form style="padding-top:10px" id="cum_form_query">
<div class="fancybox-item fancybox-close" style="position:absolute;top:5px;right:5px;"></div>
<input type="text" autofocus placeholder="Search..." style="margin-right:5px;padding:5px;">
<button type="submit" style="padding:5px;box-sizing:border-box;height:30px;cursor:pointer;">Search</button>
</form>
<hr style="margin-top:10px">
<form style="padding-top:10px" id="cum_form_checkbox">
${highlight_keywords.map((keyword, index) => `
<label>
<input type="checkbox" name="${keyword}" style="padding:5px;" ${searchHighlights.includes(keyword) ? 'checked' : ''}>
${keyword}
</label>
`).join('')}
<button id="cum_search_checkbox" type="button" style="display:block;padding:5px;box-sizing:border-box;height:30px;cursor:pointer;width:100%;margin:10px 0;">Search selected</button>
</form>
<button id="cum_search_all" type="button" style="display:block;padding:5px;box-sizing:border-box;height:30px;cursor:pointer;width:100%;margin-top:5px;" title="Search, ${highlight_keywords.join(", ")}">Search all highlights</button>
`;
document.body.insertBefore(dialog, document.body.firstChild);
console.log("Dialog added to body.");
dialog.querySelector('#cum_search_checkbox').addEventListener('click', e => {
e.preventDefault();
const selectedKeywords = Array.from(dialog.querySelectorAll('input[type="checkbox"]:checked'))
.map(checkbox => checkbox.name);
saveValue("highlights_search", selectedKeywords)
console.log(selectedKeywords)
displayResults(selectedKeywords);
document.body.removeChild(dialog);
});
dialog.querySelector('#cum_form_query').addEventListener('submit', e => {
e.preventDefault();
const input = dialog.querySelector('input').value;
if (input) displayResults(input);
document.body.removeChild(dialog);
});
dialog.querySelector('#cum_search_all').addEventListener('click', e => {
e.preventDefault();
displayResults("");
document.body.removeChild(dialog);
});
dialog.querySelector('.fancybox-close').addEventListener('click', e => {
e.preventDefault();
document.body.removeChild(dialog);
});
}
async function displayResults(query) {
try {
const resultList = document.createElement('div');
resultList.id = 'cum_search_results';
resultList.className = "list-videos";
const loadingContainer = document.createElement('div');
loadingContainer.style.textAlign = 'center';
loadingContainer.innerHTML = '<img src="https://samherbert.net/svg-loaders/svg-loaders/three-dots.svg" alt="Loading..." style="width:50px;height:50px;">';
document.querySelector("#list_videos_uploaded_videos").appendChild(loadingContainer);
const results = await videoSearch(userVideosH2, userId, query);
loadingContainer.remove();
resultList.innerHTML = `<div class="headline" style="padding:15px 5px 7px 5px;"><h2>Search results</h2></div>`;
if (!results.length) {
resultList.textContent = "No results found";
} else {
resultList.innerHTML += results.map(video => `
<div class="item" style="margin-left:11px;">
<a href="${video.url}" title="${video.title}">
<div class="img"><img class="thumb lazy-load" src="${video.imgSrc}" alt="${video.title}" width="180" height="135"></div>
<strong class="title">${video.title}</strong>
<div class="wrap"><div class="duration">${video.duration}</div></div>
</a>
</div>`).join('');
}
const existingResult = document.querySelector("#cum_search_results");
if (existingResult) existingResult.replaceWith(resultList);
else document.querySelector("#list_videos_uploaded_videos").insertAdjacentElement('afterend', resultList);
} catch (error) {
console.error("Error:", error);
}
}
async function videoSearch(userVideosH2, uid, query) {
let searchTerms = null;
let totalVideoPages = getRoundedTotalPages(userVideosH2);
let approxTotalVideos = totalVideoPages * 5;
let timeout;
if (approxTotalVideos > 1000) {
timeout = approxTotalVideos / 2;
}else if (approxTotalVideos > 500) {
timeout = approxTotalVideos / 5;
} else {
timeout = 75;
}
if (Array.isArray(query)) {
searchTerms = query.map(q => q.toLowerCase());
} else {
searchTerms = query ? [query.toLowerCase()] : highlight_keywords.map(k => k.toLowerCase());
}
if (totalVideoPages !== null) {
let resultArray = [];
let fetchPromises = [];
for (let pageInt = 1; pageInt <= totalVideoPages; pageInt++) {
let nextFivePages = userProfileAsyncURL(uid, pageInt);
let fetchPromise = new Promise((resolve) => {
setTimeout(() => {
fetchAndParseHTML(nextFivePages, (htmlDoc) => {
let videos = htmlDoc.querySelectorAll('div#list_videos_uploaded_videos_items div.item');
videos.forEach(video => {
let title = video.querySelector('a').getAttribute('title');
let url = video.querySelector('a').getAttribute('href');
let imgSrc = video.querySelector('img').getAttribute('data-original');
let duration = video.querySelector('.duration').textContent.trim();
let isPrivate = video.classList.contains('private');
let lowerTitle = title.toLowerCase();
if (searchTerms.some(term => lowerTitle.includes(term))) {
resultArray.push({ title, private: isPrivate, url, imgSrc, duration });
}
});
resolve();
});
}, timeout * (pageInt - 1));
});
fetchPromises.push(fetchPromise);
}
await Promise.all(fetchPromises);
return resultArray;
} else {
console.log("ERROR in totalVideoPages:", totalVideoPages);
return null;
}
}
function getCurrentUserVideoLocation() {
return sessionStorage.getItem(`${currentUserID()}:https://www.camwhores.tv/members/${userId}/#list_videos_uploaded_videos:params`);
}
function waitForSessionStorageChange() {
const initialValue = getCurrentUserVideoLocation();
const interval = setInterval(() => {
if (getCurrentUserVideoLocation() !== initialValue) {
clearInterval(interval);
updateVideoH2();
addSearchButton();
}
}, 50);
}
if (recentlyOnline) {
h1.textContent += " (online)";
h1.className = "online";
}
userDescription.textContent = userDescription.textContent.trim();
if (countryCode && countryName !== "no info") {
country.remove();
const img = document.createElement('img');
img.src = `https://flagcdn.com/${countryCode}.svg`;
img.width = 35;
img.alt = countryName;
img.title = countryName;
img.style.cssText = "position:absolute;top:50%;transform:translateY(-50%);left:4rem;";
parent.appendChild(img);
parent.style.position = "relative";
}
updateVideoH2();
// addButtonEventListener();
new MutationObserver(() => addSearchButton())
.observe(document.body, { childList: true, subtree: true });
if (window.location.href.endsWith("#friends") && !window.location.href.includes("add_to_friends_done")) {
var userVideoData,lastCumTitle,lastVidCode,lastUserID;
let addButton = document.querySelector('a[href="#friends"][data-action="add_to_friends"]');
let textarea = document.getElementById('add_to_friends_message');
let userId = window.location.href.split('/').slice(-2, -1)[0];
let confirmButton = document.querySelector('input[name="confirm"]');
let retrievedData = JSON.parse(localStorage.getItem('last_cum_video_data')) || {};
console.log("uid:",userId,retrievedData[userId])
if (retrievedData[userId]) {
userVideoData = retrievedData[userId];
console.log(userVideoData);
lastCumTitle = userVideoData.title;
lastVidCode = userVideoData.videoId;
lastUserID = userVideoData.userId;
} else {
console.log("No data found for this user.");
}
let pageTitle = document.title;
let username = pageTitle.replace("'s Page", "");
if (textarea !== null
&& !addButton.classList.contains('done')
// && username !== lastAutoFriend
&& lastCumTitle !== null
&& addButton !== null) {
textarea.value = "";
textarea.value += friend_request_text + " " + lastCumTitle + "\n##" + lastVidCode;
// addButton.click();
document.querySelector('input[type="submit"].submit[value="Invite"]').click(); // final submit button
}
// console.log(confirmButton)
// if(confirmButton) confirmButton.click();
if (addButton.classList.contains('done') && history.length === 1) {
window.close();
}
// console.log("Should add?")
}
else if (window.location.href.includes("add_to_friends_done")) {
let addButton = document.querySelector('a[href="#friends"][data-action="add_to_friends"]');
if (addButton && !addButton.classList.contains('done')) {
addButton.classList.add('done');
}
if (history.length === 1 || !document.hasFocus()) window.close();
}
}
if (window.location.href.startsWith("https://www.camwhores.tv/videos/")) {
const el = document.querySelector('body > div.container > div.content > div.block-video > div.video-holder > div.player > div > div > span') ||
document.querySelector('body > div.container > div.content > div.block-album > div.album-holder > div.images > span.message');
const href = document.querySelector('#tab_video_info > div > div.block-user > div > a').href;
const userId = document.querySelector('.username a')?.getAttribute('href').split('/').slice(-2, -1)[0];
if (el) {
fetch(href)
.then(response => response.text())
.then(html => {
const doc = new DOMParser().parseFromString(html, 'text/html');
const infoMsg = doc.querySelector('.info-message');
const headline = document.querySelector('body > div.container > div.content > div.headline > h1');
const isFriends = infoMsg?.textContent.includes("is in your friends list.");
const isPending = window.location.href.includes("please_check_friend");
if (isFriends) {
el.textContent = "You're already friends! Reloading in 4 seconds";
setTimeout(() => location.reload(), 4000);
} else if (isPending) {
el.innerHTML += "<br><br>Friendship confirmed? Reloading in 4 seconds";
setTimeout(() => location.reload(), 4000);
} else if (headline) {
friendRequestSetup(headline.textContent.trim(), window.location.href, userId);
el.innerHTML += `<br><a class="button" href="${el.querySelector('a').href}#friends" style="display:inline-block;">Send request</a>`;
}
document.querySelectorAll('.block-screenshots .item.private').forEach(el => {
const img = el.querySelector('img');
if (img) {
el.replaceWith(Object.assign(document.createElement('a'), {
innerHTML: el.innerHTML,
href: img.dataset.original.replace('/videos_screenshots/', '/videos_sources/').replace('/180x135/', '/screenshots/'),
className: 'item',
rel: 'screenshots',
'data-fancybox-type': 'image'
}));
}
});
})
.catch(console.error);
}
function waitForElementExists(parent, selector) {
return new Promise(resolve => {
new MutationObserver((_, observer) => {
const el = parent.querySelector(selector);
if (el) {
resolve(el);
observer.disconnect();
}
}).observe(parent, { childList: true, subtree: true });
});
}
function addDownloadLink(video) {
if (document.getElementById('downloadVideoLink')) return;
const url = video.getAttribute('src');
const name = `${document.querySelector('.headline').innerText}.mp4`;
document.querySelector('div.video-holder > div.video-info > div > div.info-buttons > div.tabs-menu > ul')
.insertAdjacentHTML('afterbegin', `<li><a id="downloadVideoLink" class="toggle-button" href="${url}" download="${name}">Download</a></li>`);
}
waitForElementExists(document.body, 'video').then(addDownloadLink).catch(console.error);
function addTimestampsToVideo(timestamps){
var tabScreenshots = document.getElementById('tab_screenshots');
var timestampsDiv = document.getElementById('timestamps');
if (timestampsDiv) return;
// Check if #tab_screenshots exists
// Check if #timestamps exists inside #tab_screenshots
timestampsDiv = document.createElement('div');
timestampsDiv.id = 'timestamps';
timestampsDiv.style = 'display:flex;gap:.1rem';
tabScreenshots.appendChild(timestampsDiv);
timestamps.forEach(function (timestamp, index) {
var formattedTimestamp = formatTimeFromSeconds(timestamp);
// Create button for timestampsDiv
var button1 = document.createElement('button');
button1.textContent = formattedTimestamp;
button1.addEventListener('click', function () {
flowplayer().seek(timestamp);
});
timestampsDiv.appendChild(button1);
// Create button for screenshot link
var screenshotLinks = document.querySelectorAll('.block-screenshots a[rel="screenshots"]');
if (screenshotLinks[index]) {
var button2 = document.createElement('button');
button2.textContent = formattedTimestamp;
button2.style.position = 'absolute';
button2.addEventListener('click', function () {
flowplayer().seek(timestamp);
});
screenshotLinks[index].style.position = 'relative';
screenshotLinks[index].appendChild(button2);
}
});
}
function waitForVideoDuration(maxAttempts, intervalMs) {
var attempts = 0;
// Approximated percentages
var percentages = [/* 0:14, */ 25, 40, 59.75, 79]; // CONSIDER 0:14 IN FRONT IF EVERYTHING! (15:49)
function checkDuration() {
var player = flowplayer();
// console.log(player, typeof player.video.duration === 'number')
if (player && player.video && typeof player.video.duration === 'number') {
var videoDuration = player.video.duration;
// Calculate percentages based on approximations
var calculatedPercentages = percentages.map(function (percent) {
return (percent / 100) * videoDuration;
});
console.log("Video Duration:", videoDuration);
console.log("Calculated Percentages:", calculatedPercentages);
// Determine the first timestamp as the minimum between 14 seconds and 5% of video duration
var firstTimestamp = 14;
// Create buttons with timestamps
var timestamps = [firstTimestamp].concat(calculatedPercentages); // Include the first timestamp
addTimestampsToVideo(timestamps);
} else {
attempts++;
setTimeout(checkDuration, intervalMs);
}
}
checkDuration();
}
// Helper function to format time from seconds to mm:ss
function formatTimeFromSeconds(seconds) {
var date = new Date(null);
date.setSeconds(seconds);
return date.toISOString().substr(14, 5);
}
waitForVideoDuration(10, 500);
document.addEventListener('visibilitychange', function() {
if (document.getElementById('tab_screenshots')) return;
if (document.visibilityState === 'visible') {
console.log("retrying timestamps")
waitForVideoDuration(10, 500);
}
});
}
if (window.location.href.startsWith("https://www.camwhores.tv/playlists/") && window.location.href !== "https://www.camwhores.tv/playlists/") {
const h1Element = document.querySelector('h1');
const playlistItem = document.querySelector('#playlist_view_playlist_view_items a');
h1Element.textContent = playlistItem.querySelector('.title').textContent.trim();
h1Element.parentElement.insertAdjacentHTML('beforeend', `<a class="button" style="padding: 5px 5px 0 5px;" href="${playlistItem.href}">Open video</a>`);
document.addEventListener('click', e => {
const clickedItem = e.target.closest('#playlist_view_playlist_view_items a');
if (clickedItem) {
h1Element.textContent = clickedItem.querySelector('.title').textContent.trim();
document.querySelector('.button').href = clickedItem.href;
}
});
}
if (window.location.href.startsWith("https://www.camwhores.tv/edit-video/")) {
document.querySelector('.section-title.expand[data-expand-id="tab_screenshots"]').click();
document.querySelectorAll("#tab_screenshots > div > div:nth-child(-n+5) > div.item-control > div.item-control-holder").forEach(el =>
el.addEventListener('click', () => {
document.querySelector('input.submit[value="Save"]').click();
if (history.length === 1) window.close();
})
);
}
if (window.location.href === "https://www.camwhores.tv/my/") {
const profileCSS = document.createElement('style');
profileCSS.className = "profile-css";
profileCSS.textContent = `
#edit_profile_about_me{height:225px;}
strong.popup-title,span.selection [role="combobox"]{display:none;}
body > div.fancybox-overlay.fancybox-overlay-fixed > div{top:0 !important;height:100svh !important;}
body > div.fancybox-overlay.fancybox-overlay-fixed,body > div.fancybox-overlay.fancybox-overlay-fixed > *{box-sizing:border-box;}
div.fancybox-inner{height:100% !important;width:max-content !important;}
.fancybox-close{top:9px !important;}
`;
document.head.appendChild(profileCSS);
}
if (window.location.href === "https://www.camwhores.tv/" || window.location.href === "https://www.camwhores.tv") {
const listVideos = document.querySelector("body > div.container > div.list_videos > div");
const headline = document.querySelector("body > div.container > div.content > div.main-content > div.headline");
const latestVideoUpSeconds = convertToSeconds(document.querySelector("#list_videos_featured_videos_items div.added").innerText);
const latestStoredUpSeconds = parseInt(localStorage.getItem("cum_last_featuredVideoUpDate"));
const showFeaturedVids = retrieveValueFromStorage("options")["cum_show_top"];
const thereIsANewerVideo = latestVideoUpSeconds < latestStoredUpSeconds;
const hideFtVideos = show => {
listVideos.style.display = show ? 'block' : 'none';
headline.style.paddingTop = show ? '20px' : '10px';
if (!show) localStorage.setItem("cum_last_featuredVideoUpDate", latestVideoUpSeconds);
document.querySelector("#🆕")?.style.setProperty("display", "none");
};
hideFtVideos(showFeaturedVids);
document.querySelector("body > div.container > div.list_videos").insertAdjacentHTML('afterbegin', `
<label class="switch" title="Top videos" id="cum_switch_top">
<input type="checkbox" id="cum_show_top" ${showFeaturedVids ? 'checked' : ''}>
<span class="slider round"></span>
</label>
`);
document.querySelector('#cum_show_top').addEventListener('change', function () {
hideFtVideos(this.checked);
saveArraySettings("options", "cum_show_top", this.checked);
});
if (!showFeaturedVids && thereIsANewerVideo) {
document.querySelector("body > div.container > div.list_videos").insertAdjacentHTML('beforeend', `
<span id="🆕" style="font-size:1.5rem;vertical-align:middle;"> 🆕</span>
`);
}
}
function extractVideoId(url) {
if(!url) return console.error("url:", url)
const parts = url.split('/');
return parts[4] + '/' + parts[5].replace(/\/$/, '');
}
function extractLatestVideoId(dom = document){
let latestVid = dom.querySelector("#list_videos_videos_list_search_result_items > div > a");
if (!latestVid) return null;
return extractVideoId(latestVid.href)
}
function currentSearchTerm() {
let string = window.location.pathname.split('/search/')[1]?.split('/')[0].trim() ||
h1.textContent.split(':')[1].trim() || null;
if (!string) return null;
return string.replace(/%20|-/g, ' ').replace(/\s+/g, ' ').toLowerCase();
}
function updateLatestVideo(dom = document, searchTerm = null){
let h1 = dom.querySelector('h1');
if (!h1) return;
if (!searchTerm) searchTerm = currentSearchTerm();
if (!searchTerm) return;
let data = {
timestamp: Date.now(),
id: extractLatestVideoId()
};
saveArraySettings("auto_search", searchTerm, data);
}
if (window.location.pathname.startsWith('/search/')) {
let searchTerm = currentSearchTerm();
console.log(searchTerm)
let hypotheticalPresence = retrieveValueFromStorage("auto_search")[searchTerm];
if(hypotheticalPresence) updateLatestVideo()
let buttonAutoSearch = document.createElement('button');
buttonAutoSearch.id = 'cum-auto-search';
buttonAutoSearch.textContent = 'Add to Wish List';
buttonAutoSearch.classList.add('btn');
buttonAutoSearch.style = "transform:translate(5px, 3px);cursor:pointer"
buttonAutoSearch.addEventListener('click', () => {
updateLatestVideo();
buttonAutoSearch.textContent = 'Done!';
buttonAutoSearch.disabled = true;
});
document.querySelector('h1')?.insertAdjacentElement('afterend', buttonAutoSearch);
}
function myVideosAsyncURL(pageInt) {
return `https://www.camwhores.tv/my/videos/?mode=async&function=get_block&block_id=list_videos_my_uploaded_videos&sort_by=&from_my_videos=${pageInt}&_=${Date.now()}`;
}
function pagesLastProcessedVideo(page, callback) {
fetchAndParseHTML(myVideosAsyncURL(page), doc => {
const items = doc.querySelectorAll("#list_videos_my_uploaded_videos_items > form > div.item.private");
const idx = Array.from(items).reverse().findIndex(item => item.classList.contains('processing'));
const checkbox = items[items.length - 1 - idx]?.querySelector(".item-control-holder .toggle-button input[type='checkbox']");
callback(checkbox ? checkbox.value : false);
});
}
async function lastProcessedVideoID(callback) {
let found = false;
const promises = Array.from({ length: 3 }, (_, page) =>
new Promise(resolve => pagesLastProcessedVideo(page + 1, value => {
if (value) found = true;
resolve(value);
}))
);
const results = await Promise.all(promises);
callback(results.find(value => value) || false);
}
function loadLastVideoData() {
return JSON.parse(localStorage.getItem('lastVideoData') || '[]') || [null, null];
}
function lastVideoDataAge(refreshMinutes = 30) {
const [, timestamp] = loadLastVideoData();
return timestamp ? (Date.now() - timestamp) / 1000 : refreshMinutes * 60 + 1;
}
function saveLastVideoID(callback) {
lastProcessedVideoID(currentID => {
localStorage.setItem('lastVideoData', JSON.stringify([currentID, Date.now()]));
callback(loadLastVideoData()[0], currentID);
console.log(`Last ID: ${loadLastVideoData()[0]}\nCurrent ID: ${currentID} (https://www.camwhores.tv/edit-video/${currentID}/)`);
});
}
function alertNewVideo(newID) {
if (confirm("A new video of yours has been published.\nGo to My Videos?")) {
window.location.href = "/my/videos/";
}
}
function updateLastVideoData(minutes = backgroundChecksMinutes) {
const waitTime = minutes * 60;
const ageOfCheck = lastVideoDataAge(backgroundChecksMinutes);
if (ageOfCheck > waitTime) {
saveLastVideoID((lastID, newID) => {
if (newID && lastID && lastID !== newID) {
alertNewVideo(newID);
} else {
console.log("No new videos were published");
}
});
} else {
// Optionally log how much time is left before the next check
// console.log(`Only ${Math.round(ageOfCheck / 60)} minutes have passed, ${minutes - Math.round(ageOfCheck / 60)} to go.`);
}
}
notifyProcessedVideos && updateLastVideoData(backgroundChecksMinutes);
function contryCodesArray() { return { "Andorra": "ad", "United Arab Emirates": "ae", "Afghanistan": "af", "Antigua and Barbuda": "ag", "Anguilla": "ai", "Albania": "al", "Armenia": "am", "Angola": "ao", "Antarctica": "aq", "Argentina": "ar", "American Samoa": "as", "Austria": "at", "Australia": "au", "Aruba": "aw", "Åland Islands": "ax", "Azerbaijan": "az", "Bosnia and Herzegovina": "ba", "Barbados": "bb", "Bangladesh": "bd", "Belgium": "be", "Burkina Faso": "bf", "Bulgaria": "bg", "Bahrain": "bh", "Burundi": "bi", "Benin": "bj", "Saint Barthélemy": "bl", "Bermuda": "bm", "Brunei": "bn", "Bolivia": "bo", "Caribbean Netherlands": "bq", "Brazil": "br", "Bahamas": "bs", "Bhutan": "bt", "Bouvet Island": "bv", "Botswana": "bw", "Belarus": "by", "Belize": "bz", "Canada": "ca", "Cocos (Keeling) Islands": "cc", "DR Congo": "cd", "Central African Republic": "cf", "Republic of the Congo": "cg", "Switzerland": "ch", "Côte d'Ivoire (Ivory Coast)": "ci", "Cook Islands": "ck", "Chile": "cl", "Cameroon": "cm", "China": "cn", "Colombia": "co", "Costa Rica": "cr", "Cuba": "cu", "Cape Verde": "cv", "Curaçao": "cw", "Christmas Island": "cx", "Cyprus": "cy", "Czechia": "cz", "Germany": "de", "Djibouti": "dj", "Denmark": "dk", "Dominica": "dm", "Dominican Republic": "do", "Algeria": "dz", "Ecuador": "ec", "Estonia": "ee", "Egypt": "eg", "Western Sahara": "eh", "Eritrea": "er", "Spain": "es", "Ethiopia": "et", "European Union": "eu", "Finland": "fi", "Fiji": "fj", "Falkland Islands": "fk", "Micronesia": "fm", "Faroe Islands": "fo", "France": "fr", "Gabon": "ga", "United Kingdom": "gb", "England": "gb-eng", "Northern Ireland": "gb-nir", "Scotland": "gb-sct", "Wales": "gb-wls", "Grenada": "gd", "Georgia": "ge", "French Guiana": "gf", "Guernsey": "gg", "Ghana": "gh", "Gibraltar": "gi", "Greenland": "gl", "Gambia": "gm", "Guinea": "gn", "Guadeloupe": "gp", "Equatorial Guinea": "gq", "Greece": "gr", "South Georgia": "gs", "Guatemala": "gt", "Guam": "gu", "Guinea-Bissau": "gw", "Guyana": "gy", "Hong Kong": "hk", "Heard Island and McDonald Islands": "hm", "Honduras": "hn", "Croatia": "hr", "Haiti": "ht", "Hungary": "hu", "Indonesia": "id", "Ireland": "ie", "Israel": "il", "Isle of Man": "im", "India": "in", "British Indian Ocean Territory": "io", "Iraq": "iq", "Iran": "ir", "Iceland": "is", "Italy": "it", "Jersey": "je", "Jamaica": "jm", "Jordan": "jo", "Japan": "jp", "Kenya": "ke", "Kyrgyzstan": "kg", "Cambodia": "kh", "Kiribati": "ki", "Comoros": "km", "Saint Kitts and Nevis": "kn", "North Korea": "kp", "South Korea": "kr", "Kuwait": "kw", "Cayman Islands": "ky", "Kazakhstan": "kz", "Laos": "la", "Lebanon": "lb", "Saint Lucia": "lc", "Liechtenstein": "li", "Sri Lanka": "lk", "Liberia": "lr", "Lesotho": "ls", "Lithuania": "lt", "Luxembourg": "lu", "Latvia": "lv", "Libya": "ly", "Morocco": "ma", "Monaco": "mc", "Moldova": "md", "Montenegro": "me", "Saint Martin": "mf", "Madagascar": "mg", "Marshall Islands": "mh", "North Macedonia": "mk", "Mali": "ml", "Myanmar": "mm", "Mongolia": "mn", "Macau": "mo", "Northern Mariana Islands": "mp", "Martinique": "mq", "Mauritania": "mr", "Montserrat": "ms", "Malta": "mt", "Mauritius": "mu", "Maldives": "mv", "Malawi": "mw", "Mexico": "mx", "Malaysia": "my", "Mozambique": "mz", "Namibia": "na", "New Caledonia": "nc", "Niger": "ne", "Norfolk Island": "nf", "Nigeria": "ng", "Nicaragua": "ni", "Netherlands": "nl", "Norway": "no", "Nepal": "np", "Nauru": "nr", "Niue": "nu", "New Zealand": "nz", "Oman": "om", "Panama": "pa", "Peru": "pe", "French Polynesia": "pf", "Papua New Guinea": "pg", "Philippines": "ph", "Pakistan": "pk", "Poland": "pl", "Saint Pierre and Miquelon": "pm", "Pitcairn Islands": "pn", "Puerto Rico": "pr", "Palestine": "ps", "Portugal": "pt", "Palau": "pw", "Paraguay": "py", "Qatar": "qa", "Réunion": "re", "Romania": "ro", "Serbia": "rs", "Russia": "ru", "Rwanda": "rw", "Saudi Arabia": "sa", "Solomon Islands": "sb", "Seychelles": "sc", "Sudan": "sd", "Sweden": "se", "Singapore": "sg", "Saint Helena, Ascension and Tristan da Cunha": "sh", "Slovenia": "si", "Svalbard and Jan Mayen": "sj", "Slovakia": "sk", "Sierra Leone": "sl", "San Marino": "sm", "Senegal": "sn", "Somalia": "so", "Suriname": "sr", "South Sudan": "ss", "São Tomé and Príncipe": "st", "El Salvador": "sv", "Sint Maarten": "sx", "Syria": "sy", "Eswatini (Swaziland)": "sz", "Turks and Caicos Islands": "tc", "Chad": "td", "French Southern and Antarctic Lands": "tf", "Togo": "tg", "Thailand": "th", "Tajikistan": "tj", "Tokelau": "tk", "Timor-Leste": "tl", "Turkmenistan": "tm", "Tunisia": "tn", "Tonga": "to", "Turkey": "tr", "Trinidad and Tobago": "tt", "Tuvalu": "tv", "Taiwan": "tw", "Tanzania": "tz", "Ukraine": "ua", "Uganda": "ug", "United States Minor Outlying Islands": "um", "United Nations": "un", "United States": "us", "Uruguay": "uy", "Uzbekistan": "uz", "Holy See (Vatican City State)": "va", "Saint Vincent and the Grenadines": "vc", "Venezuela": "ve", "British Virgin Islands": "vg", "United States Virgin Islands": "vi", "Vietnam": "vn", "Vanuatu": "vu", "Wallis and Futuna": "wf", "Samoa": "ws", "Kosovo": "xk", "Yemen": "ye", "Mayotte": "yt", "South Africa": "za", "Zambia": "zm", "Zimbabwe": "zw" } }
async function updateAlert() {
const lastCheck = await retrieveValueFromStorage('lastVersionCheckDate');
const lastVersionCheckDate = lastCheck ? new Date(lastCheck) : currentDate;
if (currentDate - lastVersionCheckDate > 3 * 24 * 60 * 60 * 1000) {
await saveValue('lastVersionCheckDate', currentDate.toString());
const htmlDoc = await fetchAndParseHTML(metaPage);
const metaContent = htmlDoc.body.textContent;
const metaVersionMatch = metaContent.match(/@version\s+([^\s]+)/);
if (metaVersionMatch) {
const latestVersion = metaVersionMatch[1];
if (latestVersion !== currentVersion) {
if (confirm('A new version of the CUM script is available.\nPlease allow automatic updates or update it manually.\nOpen in new tab?')) {
GM_openInTab("https://sleazyfork.org/en/scripts/491272-camwhores-tv-utilities-mod");
} else {
toggleChange(updateRemindersOptionName, false);
}
}
}
}
}
updateReminders && updateAlert();
blurImages &&
GM_addStyle(`
img:not(.smileys-bar img),div.logo a{
filter: blur(20px);
transition: filter 0s;
}
img:not(.smileys-bar img):hover,div.logo:hover a{
filter: blur(0px);
transition: filter .3s;
}
`);
})();