// ==UserScript==
// @name Chaturbate Enhancer
// @description Enhances Chaturbate by adding multiple new features.
// @description:de Verbessert Chaturbate durch Hinzufügen mehrerer neuer Funktionen.
// @description:es Mejora Chaturbate al agregar múltiples funciones nuevas.
// @description:es-CO Mejora Chaturbate al agregar múltiples funciones nuevas.
// @description:it Migliora Chaturbate aggiungendo più nuove funzionalità.
// @description:fr Améliore Chaturbate en ajoutant plusieurs nouvelles fonctionnalités.
// @description:ru Улучшает Chaturbate, добавляя несколько новых функций.
// @description:tr Birden çok yeni özellik ekleyerek Chaturbate'i geliştirir.
// @description:ro Îmbunătățește Chaturbate prin adăugarea de mai multe funcții noi.
// @description:no Forbedrer Chaturbate ved å legge til flere nye funksjoner.
// @description:nl Verbetert Chaturbate door meerdere nieuwe functies toe te voegen.
// @description:pl Ulepsza Chaturbate, dodając wiele nowych funkcji.
// @description:ja 複数の新機能を追加して Chaturbate を強化します。
// @description:el Βελτιώνει το Chaturbate προσθέτοντας πολλές νέες δυνατότητες.
// @description:hu Több új funkció hozzáadásával továbbfejleszti a Chaturbate szolgáltatást.
// @description:fi Parantaa Chaturbatea lisäämällä useita uusia ominaisuuksia.
// @description:ar يعزز Chaturbate عن طريق إضافة ميزات جديدة متعددة.
// @description:hi कई नई सुविधाओं को जोड़कर Chaturbate को बेहतर बनाता है।
// @description:id Meningkatkan Chaturbate dengan menambahkan beberapa fitur baru.
// @description:ko 여러 새로운 기능을 추가하여 Chaturbate를 향상시킵니다.
// @description:pt-PT Aprimora o Chaturbate adicionando vários novos recursos.
// @description:pt-BR Aprimora o Chaturbate adicionando vários novos recursos.
// @description:zh 通过添加多个新功能来增强 Chaturbate。
// @description:cs Vylepšuje Chaturbate přidáním několika nových funkcí.
// @description:sk Vylepšuje Chaturbate pridaním viacerých nových funkcií.
// @description:sl Izboljša Chaturbate z dodajanjem več novih funkcij.
// @description:sv Förbättrar Chaturbate genom att lägga till flera nya funktioner.
// @description:sr Побољшава Цхатурбате додавањем више нових функција.
// @description:af Verbeter Chaturbate deur verskeie nuwe kenmerke by te voeg.
// @description:sq Përmirëson Chaturbate duke shtuar veçori të shumta të reja.
// @description:hy Ընդլայնում է Chaturbate-ը՝ ավելացնելով բազմաթիվ նոր հնարավորություններ:
// @description:be Паляпшае Chaturbate шляхам дадання некалькіх новых функцый.
// @description:bg Подобрява Chaturbate чрез добавяне на множество нови функции.
// @description:da Forbedrer Chaturbate ved at tilføje flere nye funktioner.
// @description:et Täiustab Chaturbate'i, lisades mitu uut funktsiooni.
// @description:he משפר את Chaturbate על ידי הוספת תכונות חדשות מרובות.
// @description:hr Poboljšava Chaturbate dodavanjem više novih značajki.
// @description:fa Chaturbate را با افزودن چندین ویژگی جدید تقویت می کند.
// @description:ur متعدد نئی خصوصیات شامل کرکے Chaturbate کو بہتر بناتا ہے۔
// @description:bn একাধিক নতুন বৈশিষ্ট্য যোগ করে Chaturbate উন্নত করে।
// @version 1.2.6
// @author MoonDivision
// @license CC-BY-ND-4.0
// @copyright MoonDivision (https://sleazyfork.org/en/users/884016-moondivision)
// @namespace https://sleazyfork.org/en/users/884016-moondivision
// @homepage https://sleazyfork.org/en/scripts/441079-chaturbate-enhancer
// @supportURL https://sleazyfork.org/en/scripts/441079-chaturbate-enhancer/feedback
// @contributionURL https://chaturbate.com/in/?tour=JpRf&campaign=Nb8Yz&track=enh-contrib&next=/tipping/purchase_tokens/
// @icon https://www.google.com/s2/favicons?sz=32&domain=chaturbate.com
// @icon64 https://www.google.com/s2/favicons?sz=64&domain=chaturbate.com
// @match https://chaturbate.com/*
// @match https://*.chaturbate.com/*
// @connect camschedule.com
// @connect onechance.onelove.workers.dev
// @grant GM_addStyle
// @grant GM_addElement
// @grant GM_xmlhttpRequest
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @require https://cdn.jsdelivr.net/npm/hls.js@1
// @run-at document-body
// @noframes
// ==/UserScript==
(function() {
'use strict';
let style = `
/* Hide media overlays */
.photoVideoDetailSection img {
filter: unset !important;
}
.userUpload div {
background: none !important;
}
.psContainer .lockOverlayBg, .smContainer .lockOverlayBg {
display: none !important;
}
.userUpload img[src$="lock.svg"] {
display: none !important;
}
/* Hide ads */
.ad, .vote-banner {
display: none !important;
}
.cb-enh-avatar {
margin-left: 10px;
border: 1px solid #bfbfbf;
width: 150px;
height: 150px;
background-color: #ebebeb;
margin-bottom: 5px;
background-size: 100% 100%;
position: relative;
}
.darkmode .cb-enh-avatar {
border-color: #2d3e50;
background-color: #202c39;
}
.cb-enh-avatar, .cb-enh-avatar img {
border-radius: 150px;
}
.cb-enh-avatar img {
width: 100%;
height: 100%;
opacity: 0;
position: absolute;
left: 0;
top: 0;
-webkit-user-drag: none;
-webkit-app-region: no-drag;
user-drag: none;
app-region: no-drag;
pointer-events: none;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.cb-enh-footer {
font-size: 14px;
color: #341b00;
font-weight: bold;
}
.darkmode .cb-enh-footer {
color: #efefef;
}
.cb-enh-footer a {
color: inherit !important;
text-decoration: underline;
}
/* Enlarge media in bio */
tr:not(.smContainer) .contentText .previewBorder {
width: 190px;
height: 135px;
}
tr:not(.smContainer) .contentText .tokenText {
top: 118px !important;
right: 5px !important;
}
/* Detach floaters in "about" */
tr:not(.smContainer):not(.psContainer) .contentText img, tr:not(.smContainer):not(.psContainer) .contentText li, tr:not(.smContainer):not(.psContainer) .contentText a, tr:not(.smContainer):not(.psContainer) .contentText p {
position: unset !important;
}
.cb-enh-video {
max-width: 900px;
margin: 0px;
padding: 0px;
width: 100%;
height: 100%;
object-fit: contain;
background-color: rgba(0, 0, 0, 0);
display: inline;
border: 0;
outline: 0;
}
.cb-enh-video::-webkit-media-controls-play-button {
display: none;
}
.cb-enh-video::-webkit-media-controls-timeline {
display: none;
}
.cb-enh-video::-webkit-media-controls-current-time-display {
display: none;
}
.cb-enh-video::-webkit-media-controls-timeline-container {
display: none;
}
.cb-enh-video::-webkit-media-controls-time-remaining-display {
display: none;
}
#cb-enh-inac-load-chat {
cursor: pointer;
}
`;
GM_addStyle(style);
$(document).ready(function() {
if('initialRoomDossier' in unsafeWindow) {
enhanceRoom();
}
if(!$('#id_animate_thumbnails').is(':checked')) {
$(document).on('mouseenter', '.room_list_room img, .roomElement img, .roomCard img', function() {
if(window.currentHoverInterval) {
clearInterval(window.currentHoverInterval);
window.currentHoverInterval = null;
}
updateRoomThumb($(this));
window.currentHoverInterval = setInterval(() => {
updateRoomThumb($(this));
}, 100);
});
$(document).on('mouseleave', '.room_list_room img, .roomElement img, .roomCard img', function() {
if(window.currentHoverInterval) {
clearInterval(window.currentHoverInterval);
window.currentHoverInterval = null;
}
});
}
});
function updateRoomThumb($el) {
let uname = $el.parent().data('room');
$el.attr('src', 'https://roomimg.stream.highwebmedia.com/minifwap/' + uname + '.jpg?' + Math.random());
}
document.cookie = 'noads=1; expires=Sun, 1 Jan 9999 00:00:00 UTC; path=/';
document.cookie = 'agreeterms=1; expires=Sun, 1 Jan 9999 00:00:00 UTC; path=/';
document.cookie = 'fromaffiliate=1; expires=Sun, 1 Jan 9999 00:00:00 UTC; path=/';
document.cookie = 'affkey="eJyrViopylayUlBKzctQ0lFQSkxLA/HMiwsM03KTQCIFIL6RIYhZBGKCGCUgRnpRoQGIk5wLVuKXZBFZpVQLAEdlFCg="; expires=Sun, 1 Jan 9999 00:00:00 UTC; path=/';
document.cookie = 'noads=1; expires=Sun, 1 Jan 9999 00:00:00 UTC; path=/; domain=.chaturbate.com';
document.cookie = 'agreeterms=1; expires=Sun, 1 Jan 9999 00:00:00 UTC; path=/; domain=.chaturbate.com';
document.cookie = 'fromaffiliate=1; expires=Sun, 1 Jan 9999 00:00:00 UTC; path=/; domain=.chaturbate.com';
document.cookie = 'affkey="eJyrViopylayUlBKzctQ0lFQSkxLA/HMiwsM03KTQCIFIL6RIYhZBGKCGCUgRnpRoQGIk5wLVuKXZBFZpVQLAEdlFCg="; expires=Sun, 1 Jan 9999 00:00:00 UTC; path=/; domain=.chaturbate.com';
function enhanceRoom(ajaxTransition=false) {
$('.cb-enh-row').remove();
if(unsafeWindow.initialRoomDossier === '') {
// initialRoomDossier is set but is empty
// room might be banned or blocked for user
enhanceInaccessibleRoom();
return;
}
let intv = setInterval(function() {
if($('video.vjs-tech').length === 0) {
return;
}
// Make clicking on live video feed don't pause it anymore
$('video.vjs-tech').on('pause', function() {
try {
$('video.vjs-tech')[0].play();
} catch(err) {}
});
clearInterval(intv);
// Watch for AJAX page transition
let currentUsername = $("a.nextCamBgColor")[0].getAttribute('href').slice(6, -1);
let pageTransitionIntv = setInterval(function() {
let uname = $("a.nextCamBgColor")[0].getAttribute('href').slice(6, -1);
if(currentUsername != uname) {
clearInterval(pageTransitionIntv);
enhanceRoom(true);
currentUsername = uname;
}
}, 25);
}, 25);
let userData;
let broadcasterName;
if(!ajaxTransition) {
userData = JSON.parse(unsafeWindow.initialRoomDossier);
broadcasterName = userData.broadcaster_username;
}
else {
broadcasterName = $("a.nextCamBgColor")[0].getAttribute('href').slice(6, -1);
}
let lang = $('html').attr('lang');
let intervalId = setInterval(() => {
let $table = $('.BioContents > div > table');
if($table.length === 0) {
return;
}
clearInterval(intervalId);
// Add offline avatar
let $offlineNotice = $('.offlineRoomNotice');
if($offlineNotice.length > 0) {
insertRoomAv($offlineNotice, broadcasterName);
}
let $divSchedule = addBioRow('Schedule', false, '<div id="cb-enh-iframe"></div>');
if(userData && userData.room_status === 'offline') {
addBioRow('Last Subject', true, userData.room_title);
}
let $divRegion = addBioRow('Region', false, '<a href=""></a>');
let $divOnlineFor = addBioRow('Online For', false);
GM_xmlhttpRequest({
method: 'GET',
url: 'https://camschedule.com/api/room/' + broadcasterName + '?lang=' + lang,
timeout: 60*2*1000,
onload: function(responseDetails) {
let data;
try {
data = JSON.parse(responseDetails.responseText);
}
catch(SyntaxError) {
return;
}
// Populate "region" row
if(data['region'] !== '') {
let href = '';
if(data['region_id'] == 0) {
href = '/asian-cams/';
}
else if(data['region_id'] == 1) {
href = '/euro-russian-cams/';
}
else if(data['region_id'] == 2) {
href = '/north-american-cams/';
}
else if(data['region_id'] == 3) {
href = '/south-american-cams/';
}
else if(data['region_id'] == 4) {
href = '/other-region-cams/';
}
let elA = $divRegion.children('.cb-enh-row-value').children('a')[0];
elA.innerHTML = data['region'];
elA.href = href;
$divRegion.show();
}
// Populate "online for" row
if(data['online_for'] && data['online_for'] !== '') {
$divOnlineFor.children('.cb-enh-row-value')[0].innerHTML = data['online_for'];
$divOnlineFor.show();
}
// Add schedule
if(data['has_schedule']) {
let darkMode = $('body').hasClass('darkmode') ? 1 : 0;
let iframeWrapper = document.getElementById('cb-enh-iframe');
GM_addElement(iframeWrapper, 'iframe', {
src: 'https://camschedule.com/embed/schedule/' + broadcasterName + '?dark=' + darkMode + '&lang=' + lang,
style: 'width: 100%; height: 350px; border: 0;',
});
$divSchedule.show();
}
}
});
}, 500);
}
function enhanceInaccessibleRoom() {
let lang = $('html').attr('lang');
// Display video of inaccessible room
let $baseRoomContentDiv = $("div.BaseRoomContents div")
if($baseRoomContentDiv.length === 0) {
return;
}
if($baseRoomContentDiv.text().indexOf("Access denied") !== 0) {
return;
}
$baseRoomContentDiv.append("<br>Chaturbate Enhancer will try to load video and bio of this room.<br><br>");
let $langForm = $("form[action='/set_language/'] input[name='next']");
if($langForm.length === 0) {
return;
}
let username = $("form[action='/set_language/'] input[name='next']")[0].value.slice(1, -1);
GM_addStyle(`
.BaseRoomContents div {
font-size: 14px !important;
}
`);
let $upperHolder = $('<div></div>');
$baseRoomContentDiv.append($upperHolder);
let $videoHolder = $('<div></div>');
$baseRoomContentDiv.append($videoHolder);
let $upperHolder2 = $('<div></div>');
$baseRoomContentDiv.append($upperHolder2);
let $upperHolder3 = $('<div></div>');
$baseRoomContentDiv.append($upperHolder3);
let $infoHolder = $('<div></div>');
$baseRoomContentDiv.append($infoHolder);
let $infoHolder2 = $('<div></div>');
$baseRoomContentDiv.append($infoHolder2);
let $scheduleHolder = $('<div></div>');
$baseRoomContentDiv.append($scheduleHolder);
let isOnline = false;
// video type
GM_xmlhttpRequest({
method: 'GET',
url: 'https://onechance.onelove.workers.dev/?https://chaturbate.com/api/chatvideocontext/' + username + '/',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Referer': 'https://chaturbate.com/' + username + '/',
},
timeout: 60*1*1000,
onload: function(responseDetails) {
let data;
try {
data = JSON.parse(responseDetails.responseText);
}
catch(SyntaxError) {
return;
}
if($baseRoomContentDiv.length === 0) {
return;
}
let playVideo = true;
if(data['room_status'] !== 'public') {
let $avDiv = $("<div></div>");
$upperHolder.append($avDiv);
insertRoomAv($avDiv, username);
$upperHolder.append('Room status is: ' + data['room_status'] + '<br>');
playVideo = false;
}
else {
isOnline = true;
$("#cb-enh-inac-load-chat").show()
}
if(data['hls_source'] === '') {
playVideo = false;
}
if(data['room_title']) {
let $span = $('<span></span>');
if(data['room_status'] !== 'offline') {
$span.text('Subject: ' + data['room_title']);
$upperHolder.append($span);
}
else {
$span.text('Last Subject: ' + data['room_title']);
$upperHolder.append($span);
}
$upperHolder.append('<br><br>');
}
if(playVideo) {
let $video = $('<video controls webkit-playsinline playsinline autoplay muted data-listener-count-webkitendfullscreen="1" class="vjs-tech cb-enh-video" id="vjs_video_3_html5_api" tabindex="-1" role="application" poster="https://cbjpeg.stream.highwebmedia.com/stream?room=' + username + 'f=' + Math.random() + '"></video>');
$videoHolder.append($video);
$video.on('pause', function() {
try {
$video[0].play();
} catch(err) {}
});
$video.on('click', function() {
try {
$video[0].play();
} catch(err) {}
});
let hls = new Hls();
hls.loadSource(data['hls_source']);
hls.attachMedia($video[0]);
}
if(data['age']) {
$upperHolder3.append('<br><br>Age: ' + data['age'] + '<br>');
}
if(data['broadcaster_gender']) {
$upperHolder3.append('Gender: ' + data['broadcaster_gender'] + '<br>');
}
if(data['num_viewers']) {
if(data['room_status'] !== 'offline') {
$upperHolder3.append('Viewers: ' + data['num_viewers'] + '<br>');
}
else {
$upperHolder3.append('Last Viewers: ' + data['num_viewers'] + '<br>');
}
}
if(data['performer_has_fanclub']) {
$infoHolder2.append('Has Fanclub: Yes<br>');
}
else {
$infoHolder2.append('Has Fanclub: No<br>');
}
if('satisfaction_score' in data) {
let sc = data['satisfaction_score'];
if('percent' in sc && 'up_votes' in sc && 'down_votes' in sc) {
$infoHolder2.append('Satisfaction Score: ' + sc['percent'] + '% (' + sc['up_votes'] + ' up, ' + sc['down_votes'] + ' down)<br>');
}
}
},
onerror: function() {
$upperHolder.append('<br>ERROR: Unable to load video.');
}
});
// Fetch info about room
GM_xmlhttpRequest({
method: 'GET',
url: 'https://camschedule.com/api/room/' + username + '?lang=' + lang,
timeout: 60*2*1000,
onload: function(responseDetails) {
let data;
try {
data = JSON.parse(responseDetails.responseText);
}
catch(SyntaxError) {
return;
}
let $loadChatHref = $('<a id="cb-enh-inac-load-chat" style="display:none;">Click here to try to load chat.</a><br>');
$upperHolder2.append($loadChatHref);
if(isOnline) {
$loadChatHref.show();
}
$loadChatHref.on('click', function(e) {
$loadChatHref.hide();
e.preventDefault();
e.stopPropagation();
$videoHolder.empty();
GM_addElement($videoHolder[0], 'iframe', {
src: 'https://onechance.onelove.workers.dev/?https://chaturbate.com/embed/' + username + '/',
style: 'width: 100%; max-width: 1400px; height: 700px; border: 0; border-radius: 4px;',
});
});
// Populate "region" row
if(data['region'] !== '') {
let href = '';
if(data['region_id'] == 0) {
href = '/asian-cams/';
}
else if(data['region_id'] == 1) {
href = '/euro-russian-cams/';
}
else if(data['region_id'] == 2) {
href = '/north-american-cams/';
}
else if(data['region_id'] == 3) {
href = '/south-american-cams/';
}
else if(data['region_id'] == 4) {
href = '/other-region-cams/';
}
$infoHolder.append('Region: <a href="' + href + '">' + data['region'] + '</a><br>');
}
// Populate "online for" row
if(data['online_for'] && data['online_for'] !== '') {
$infoHolder.append('Online For: ' + data['online_for'] + '<br>');
}
else if(data['last_online'] && data['last_online'] !== '') {
$infoHolder.append('Last Online: ' + data['last_online'] + '<br>');
}
let info = {
'real_name': 'Real Name',
'birthday': 'Birthday',
'followers_f': 'Followers',
'location': 'Location',
'languages': 'Languages',
'smoke_drink': 'Smoke / Drink',
'body_type': 'Body Type',
'body_decorations': 'Body Decorations',
};
Object.keys(info).forEach(function(k) {
let v = info[k];
if(data[k]) {
let $span = $('<span></span>');
$span.text(v + ': ' + data[k]);
$infoHolder.append($span);
$infoHolder.append('<br>');
}
});
// Add schedule
if(data['has_schedule']) {
$scheduleHolder.append('Schedule: <br>');
let darkMode = $('body').hasClass('darkmode') ? 1 : 0;
GM_addElement($scheduleHolder[0], 'iframe', {
src: 'https://camschedule.com/embed/schedule/' + username + '?dark=' + darkMode + '&lang=' + lang,
style: 'width: 100%; height: 350px; border: 0;',
});
}
}
});
}
function addBioRow(name, visible = true, value = '') {
let $el = $('<tr class="cb-enh-row" style="' + (visible ? '' : 'display: none; ') + 'font-size: 14px; font-weight: normal; line-height: 15px; vertical-align: top; text-align: left;"><td class="label" style="padding-bottom: 9px; font-family: UbuntuMedium, Arial, Helvetica, sans-serif; height: 16px;"><span>' + name + ':</span></td><td class="contentText cb-enh-row-value" style="font-size: 14px; line-height: 16px; font-family: UbuntuRegular, Arial, Helvetica, sans-serif;">' + value + '</td></tr>');
let $psContainers = $('.BioContents > div > table > .psContainer');
let $smContainers = $('.BioContents > div > table > .smContainer');
if($psContainers.length > 0) {
$psContainers.last().after($el);
}
else if($smContainers.length > 0) {
$smContainers.last().after($el);
}
else {
$('.BioContents > div > table > tr').slice(-2).first().after($el);
}
return $el;
}
function insertRoomAv($div, username) {
$div.prepend('<div class="cb-enh-avatar"></div>');
GM_addElement($('.cb-enh-avatar')[0], 'img', {
src: 'https://camschedule.com/assets/img/avatar.png',
alt: '',
onload: 'this.style.opacity=1'
});
GM_addElement($('.cb-enh-avatar')[0], 'img', {
src: 'https://thumbv.camschedule.com/av/' + username + '.jpg',
alt: '',
onload: 'this.style.opacity=1'
});
}
})();