// ==UserScript==
// @name Camwhores.tv Utilities Mod
// @namespace https://sleazyfork.org/users/1281730-vipprograms
// @version 0.12
// @description Information preview inside messages, removal of private lock from videos uploaded by friends, and more
// @author vipprograms
// @match https://www.camwhores.tv/*
// @grant GM_xmlhttpRequest
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAMUExURQAAAP8ANwwA/////7gbQJkAAAABdFJOUwBA5thmAAAAAWJLR0QDEQxM8gAAAAd0SU1FB+gDHhIuCjXV/h8AAAA4SURBVAjXY2ANDQ1gEA0NDWEIYWBgZAhgAAIUghEiC1YHBhpMDRpIhBbXghUMXKtWLWBgWqHVAACjlwz/pN0YPwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyNC0wMy0zMFQxODo0NjowOSswMDowME+iXNIAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjQtMDMtMzBUMTg6NDY6MDkrMDA6MDA+/+RuAAAAKHRFWHRkYXRlOnRpbWVzdGFtcAAyMDI0LTAzLTMwVDE4OjQ2OjEwKzAwOjAwMNiA/AAAAABJRU5ErkJggg==
// ==/UserScript==
(function() {
'use strict';
const style = document.createElement('style');
style.textContent = `
ul > li.next,
ul > li.prev {
display: list-item !important;
}
`;
document.head.appendChild(style);
if (window.location.href == "https://www.camwhores.tv/my/messages/") {
// Function to apply styles to unread notifications
// Function to apply styles to unread notifications
function applyStylesToNotifications() {
console.log("Applying styles to notifications");
var unreadNotifications = document.querySelectorAll('.unread-notification');
unreadNotifications.forEach(function(notification) {
notification.style.top = "0";
notification.style.left = "0";
});
}
// Function to process thumbnails
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 + '"]');
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.textContent = "Friends ✅";
divElement.style.position = "absolute";
divElement.style.top = "0";
divElement.style.right = "0";
divElement.style.backgroundColor = "black";
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.")){
var 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) {
var 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;
}
}
var divElement = document.createElement('div');
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";
if (usernameLink) {
var firstDivElement = usernameLink.querySelector('div.img');
if (firstDivElement) {
firstDivElement.appendChild(divElement);
}
}
}
});
} 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 to add event listener to buttons
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");
setTimeout(function() {
applyStylesToNotifications();
processThumbnails();
}, 1000);
}
});
}
// Call the functions to apply styles and process thumbnails
console.log("Calling functions to apply styles and process thumbnails");
// Delay execution of initialization code by 1 second
setTimeout(function() {
applyStylesToNotifications();
var thumbnailsProcessed = processThumbnails();
if (!thumbnailsProcessed) {
console.log("Processing thumbnails failed. Retrying in 1000 milliseconds.");
setTimeout(function() {
processThumbnails(); // Retry processing thumbnails
}, 1000);
}
addButtonEventListener();
}, 1000);
}else if(window.location.href.startsWith("https://www.camwhores.tv/my/messages/")) {
// Function to fetch the URL and extract title, content, and joined date
function fetchAndExtract(url) {
GM_xmlhttpRequest({
method: "GET",
url: url,
onload: function(response) {
// Parse the response text into a HTML document
var parser = new DOMParser();
var htmlDoc = parser.parseFromString(response.responseText, "text/html");
// Get the text of the specified elements
var titleElement = htmlDoc.evaluate('//*[@id="list_videos_uploaded_videos"]/div[1]/h2', htmlDoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
var contentElement = htmlDoc.evaluate('//*[@id="list_videos_uploaded_videos_items"]/div[1]/a/div[3]/div[1]/em', htmlDoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
var joinedDateElement = htmlDoc.evaluate('/html/body/div[2]/div[2]/div/div[2]/div[2]/div/div/div[1]/div[3]/em', htmlDoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
var joinedDateText = joinedDateElement.singleNodeValue.textContent;
// Check if the elements exist
if (titleElement && titleElement.singleNodeValue && contentElement && contentElement.singleNodeValue && joinedDateElement && joinedDateElement.singleNodeValue) {
var titleText = titleElement.singleNodeValue.textContent;
// Extract text inside parentheses from title
var extractedTitle = titleText.match(/\(([^)]+)\)/);
if (extractedTitle && extractedTitle.length > 1) {
var contentText = contentElement.singleNodeValue.textContent;
var generatedText = "<div id=gen_joined_text>Joined " + joinedDateText + "<br>" + extractedTitle[1] + " videos, last " + contentText + "</div>";
// Add generated text to the visible page
var messageDiv = document.evaluate('//*[@id="list_messages_my_conversation_messages_items"]/div/div[3]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
if (messageDiv && messageDiv.singleNodeValue) {
messageDiv.singleNodeValue.innerHTML += generatedText;
}
} else {
console.log('Text inside parentheses in title not found.');
}
} else {
console.log('Title, content, or joined date element not found. Probably no videos uploaded yet.');
// Add "No videos" to the visible page
var messageDiv = document.evaluate('//*[@id="list_messages_my_conversation_messages_items"]/div/div[3]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
if (messageDiv && messageDiv.singleNodeValue) {
messageDiv.singleNodeValue.innerHTML += "<div id=gen_joined_text>Joined " + joinedDateText + "<br>" + "<b>No videos</b></div>";
}
}
}
});
}
// Get the href attribute of the first message
var messageLink = document.evaluate('//*[@id="list_messages_my_conversation_messages_items"]/div/div[1]/a', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
// Check if the element exists
if (messageLink && messageLink.singleNodeValue) {
// Get the URL from the message link
var messageUrl = messageLink.singleNodeValue.href;
// Fetch and extract title, content, and joined date from the URL
fetchAndExtract(messageUrl);
} else {
console.log('Message link not found.');
}
// Remove class "bottom" from specified element
var bottomElement = document.evaluate('//*[@id="list_messages_my_conversation_messages_items"]/div/div[3]/form/div', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
if (bottomElement && bottomElement.singleNodeValue) {
bottomElement.singleNodeValue.style.marginTop = "1em";
bottomElement.singleNodeValue.style.marginBottom = "1em";
bottomElement.singleNodeValue.classList.remove("bottom");
} else {
console.log('Bottom element not found.');
setTimeout(function() {
var gen_joined_text = document.getElementById('gen_joined_text');
gen_joined_text.innerHTML = "<br>" + gen_joined_text.innerHTML;
// gen_joined_text.style.marginTop = "1em";
// gen_joined_text.classList.add("custom-margin-top");
}, 200); // Adjust the delay time as needed
}
}else if(window.location.href !== "https://www.camwhores.tv/my/videos/") {
// Set to store unique video elements
const processedVideos = new Set();
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);
// Add the video element to the set of processed videos
processedVideos.add(parentDiv.parentElement);
}
});
return private_videos;
}
async function checkVideoPage(url) {
try {
const response = await fetch(url);
// Check if the response status code indicates an error
if (!response.ok) {
console.error('Error loading video page:', response.status);
return true; // Return true if there's an error loading the page
}
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;
// body > div.container > div.content > div.block-video > div.video-holder > div.player > div > div > span
// basically the "This video is a private video uploaded by" message
const hasElement2 = doc.querySelector('body > div.container > div.content > div.block-album > div.album-holder > div.images > span.message') !== null;
return hasElement1 || hasElement2;
} catch (error) {
console.error('Error checking video page:', error);
return false;
}
}
async function applyStyleToPrivateVideos() {
const privateVideos = addPrivateVideos();
for (const video of privateVideos) {
const videoLink = video.getAttribute('href');
// console.log(videoLink);
const hasElement = await checkVideoPage(videoLink);
// console.log(videoLink + " " + hasElement);
if (!hasElement) {
video.style.outline = '2px solid green';
const linePrivate = video.querySelector('div > .line-private');
if (linePrivate) {
linePrivate.style.display = 'none';
}
const imgElement = video.querySelector('div > img');
if (imgElement) {
imgElement.style.opacity = '1';
}
}else{
// add ❌
const linePrivate = video.querySelector('div > .line-private > span');
if (linePrivate) {
linePrivate.textContent += ' ❌';
}
}
await new Promise(resolve => setTimeout(resolve, 25)); // avoid 503
}
}
// Call the applyStyleToPrivateVideos function initially
applyStyleToPrivateVideos();
// Set interval to repeatedly check for private videos and apply styling
setInterval(() => {
applyStyleToPrivateVideos();
}, 1000); // Interval in milliseconds (every second)
if (window.location.href.startsWith("https://www.camwhores.tv/members/")) {
console.log("NOT messages, user PROFILE");
function updateH2Element() {
const h2Element = document.querySelector('#list_videos_uploaded_videos .headline h2');
if (h2Element) {
const text = h2Element.textContent;
const startIndex = text.indexOf('(');
const endIndex = text.indexOf(')', startIndex);
if (startIndex !== -1 && endIndex !== -1 && text.includes("Page")) {
const videos_total = parseInt(text.substring(startIndex + 1, endIndex));
const rounded_videos_total = Math.ceil(videos_total / 5);
const innerHTML = h2Element.innerHTML.trim();
if (!innerHTML.includes('/')) {
h2Element.innerHTML = innerHTML + '/' + rounded_videos_total;
}
}
}
}
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");
setTimeout(function() {
updateH2Element()
}, 750);
}
});
}
updateH2Element()
addButtonEventListener()
}
if(window.location.href.startsWith("https://www.camwhores.tv/videos/")){
const element1 = document.querySelector('body > div.container > div.content > div.block-video > div.video-holder > div.player > div > div > span');
const element2 = document.querySelector('body > div.container > div.content > div.block-album > div.album-holder > div.images > span.message');
if (element1 !== null || element2 !== null) {
const href = document.querySelector('#tab_video_info > div > div.block-user > div > a').href;
fetch(href)
.then(response => response.text())
.then(html => {
const htmlDoc = new DOMParser().parseFromString(html, 'text/html');
const infoMessage = htmlDoc.querySelector('.info-message');
if (infoMessage && infoMessage.textContent.includes("is in your friends list.")) {
const friends_string = "You're already friends! Reloading in 4 seconds";
(element1 || element2).textContent = friends_string;
setInterval(() => {
location.reload();
}, 4000);
} else {
console.log("Not friends!");
}
})
.catch(error => console.error('Error fetching:', error));
}
}
}
})();