Sleazy Fork is available in English.

Chaturbate MULTI-CAM Viewer

Adds a new tab to Chaturbate, and allows you to watch multiple webcams at once

  1. // ==UserScript==
  2. // @name Chaturbate MULTI-CAM Viewer
  3. // @namespace https://tesomayn.com
  4. // @description Adds a new tab to Chaturbate, and allows you to watch multiple webcams at once
  5. // @version 2.0.2
  6. // @include http://chaturbate.com/
  7. // @include http://chaturbate.com/#*
  8. // @include http://chaturbate.com/#live*
  9. // @include http://chaturbate.com/*-cams/*
  10. // @include http://*.chaturbate.com/
  11. // @include http://*.chaturbate.com/#*
  12. // @include http://*.chaturbate.com/*-cams/*
  13. // @include https://chaturbate.com/
  14. // @include https://chaturbate.com/#*
  15. // @include https://chaturbate.com/#live*
  16. // @include https://chaturbate.com/*-cams/*
  17. // @include https://*.chaturbate.com/
  18. // @include https://*.chaturbate.com/#*
  19. // @include https://*.chaturbate.com/*-cams/*
  20. // @exclude http://serve.ads.chaturbate.com/*
  21. // @require https://code.jquery.com/jquery-2.1.4.min.js
  22. // @require https://greasyfork.org/scripts/14852-patreon-lib/code/Patreonlib.js?version=93835
  23. // @grant unsafeWindow
  24. // @grant GM_registerMenuCommand
  25. // @grant GM_openInTab
  26. // @grant GM_getValue
  27. // @grant GM_setValue
  28. // @license MIT
  29. // ==/UserScript==
  30.  
  31. $(function() {
  32. if (window.top != window.self)
  33. return;
  34.  
  35. var gm = function(){
  36.  
  37. var self = this;
  38.  
  39. this.STORAGE_KEY_NAME = "chaturbate_girls";
  40. this.LAYOUT_KEY_NAME = "chaturbate_layout";
  41.  
  42. this.get_layout = function(){
  43. setTimeout(function(){
  44. var temp = 2;
  45. var layout_id = GM_getValue(self.LAYOUT_KEY_NAME);
  46. if (typeof layout_id == "undefined")
  47. layout_id = temp;
  48.  
  49. var adder = function(lid){
  50. viewer.layout_id = lid;
  51. viewer.layout(lid);
  52. }
  53. var script = document.createElement("script");
  54. script.textContent = "(" + adder.toString() + ")("+layout_id+");";
  55. //document.body.appendChild(script);
  56. },0);
  57. }
  58.  
  59. this.get_girls = function(){
  60. setTimeout(function(){
  61. var temp = '[]';
  62. var sJSON = GM_getValue(self.STORAGE_KEY_NAME);
  63. if (typeof sJSON == "undefined")
  64. sJSON = temp;
  65. var adder = function(savedGirls){
  66. $.each(savedGirls,function(){
  67. viewer.girls.push(new Girl(this));
  68. });
  69. if ( location.hash == "#live" )
  70. viewer.show();
  71. }
  72. var script = document.createElement("script");
  73. script.textContent = "(" + adder.toString() + ")("+sJSON+");";
  74. document.body.appendChild(script);
  75.  
  76. },0);
  77. }
  78.  
  79. this.set_girls = function(){
  80. setTimeout(function(){
  81. var data = JSON.stringify(unsafeWindow.jQuery.map(unsafeWindow.viewer.girls,function(o){ return o.username }));
  82. GM_setValue(self.STORAGE_KEY_NAME, data);
  83. },0);
  84. }
  85.  
  86. this.set_layout = function(){
  87. setTimeout(function(){
  88. GM_setValue(self.LAYOUT_KEY_NAME, unsafeWindow.viewer.layout_id);
  89. },0);
  90. }
  91. return self;
  92. };
  93.  
  94. if (cloneInto){
  95. var insideGM = new gm();
  96. var outsideGM = createObjectIn(unsafeWindow, {defineAs: "gm"});
  97. Object.keys(insideGM).forEach(function(key){
  98. try {
  99. if (typeof insideGM[key] == 'function'){
  100. exportFunction(insideGM[key], outsideGM, {defineAs: key});
  101. }
  102. } catch(e){
  103.  
  104. }
  105. });
  106. }
  107. else {
  108. unsafeWindow.gm = new gm;
  109. }
  110.  
  111. function main() {
  112. if (typeof jQuery != "undefined"){
  113. jQuery(document).ready(function(){
  114. function getKey(e) {
  115. if(window.event) { // IE
  116. return e.keyCode;
  117. } else if(e.which) { // Netscape/Firefox/Opera
  118. return e.which
  119. }
  120. }
  121.  
  122. var exports = "getKey,toHtml,websiteHostName,Girl,viewer";
  123.  
  124. var toHtml = function(data, template){
  125. return template.replace(/#(?:\{|%7B)(.*?)(?:\}|%7D)/g, function($1, $2){
  126. return ($2 in data) ? data[$2] : '';
  127. })
  128. }
  129.  
  130. var websiteHostName = location.protocol + "//" + location.host + "/";
  131.  
  132. var Girl = function(name){
  133. var user = name.replace(/\//g,"");
  134. var self = this;
  135. this.href = websiteHostName + user;
  136. this.username = user;
  137. this.src = websiteHostName + "embed/" + self.username + "/?join_overlay=1&room=" + self.username;
  138. }
  139.  
  140.  
  141. var viewer = new (function(){
  142.  
  143. var self = this;
  144.  
  145. var list_template = '<li id="#{username}">'+
  146. ' <img src="http://ccstatic.highwebmedia.com/static/images/ico-01.png" class="remove" onclick="viewer.remove(\'#{username}\',this)">'+
  147. ' <a target="_blank" href="#{href}"><img src="http://ccstatic.highwebmedia.com/static/images/ico-cams.png" class="handle" title="#{username}"></a>'+
  148. ' <iframe src="#{src}"></iframe>'+
  149. ' </li>';
  150.  
  151. this.layout_id = 2;
  152. this.girls = [];
  153. this.all_girls = [];
  154. this.loaded = false;
  155.  
  156. this.init = function(){
  157.  
  158. $('.content').prepend("<div style='width:1500px; margin:3px 32px; padding:3px; border:1px solid #CCC;'> Use the <img src='http://ccstatic.highwebmedia.com/static/images/ico-cams.png' align='absmiddle'> icon to add girls to the 'MULTI-CAM' tab </div>");
  159.  
  160. var template = '<div id="camGirls" style="visibility:hidden;">'+
  161. '<div id="camControls">'+
  162. 'Username: <input type="text" name="camGirlUsername" id="camGirlUsername" onkeyup="if (getKey(event) == 13) viewer.add()" >'+
  163. '<input type="Button" value="Add" onclick="viewer.add()">'+
  164. '<input type="Button" value="Add Top 12" onclick="viewer.addTop12()">'+
  165. '<input type="Button" value="Remove All" onclick="viewer.removeAll()">'+
  166. '<input type="Button" value="Remove Offlines" onclick="viewer.clearEmptyCams()">'+
  167. '<input type="Button" value="Save" onclick="viewer.save()">'+
  168. '[ Layout: '+
  169. '<input type="Button" value="Semi-Compact" onclick="viewer.layout(1)" id="layout_1">'+
  170. '<input type="Button" value="Compact" onclick="viewer.layout(2)" id="layout_2">'+
  171. '<input type="Button" value="Full" onclick="viewer.layout(3)" id="layout_3">]'+
  172. '</div>'+
  173. '<ul id="girls_list"></ul>'+
  174. '</div>';
  175. $("#main .content").after(template);
  176.  
  177. var css = '<style type="text/css">' +
  178. '#camGirls ul { margin: 0; padding:0; display:inline-block;}'+
  179. '#camGirls li { margin: 0; padding:0; width:500px; overflow:hidden; display:inline-block; height:456px; }'+
  180. '#camGirls iframe { margin: 0; padding:0; border:none; position:relative; width:1030px; height:528px; }'+
  181. '#camGirls .remove { cursor:pointer; display:inline; top:2px; left:1px; position:relative; float:left; z-index:99; }'+
  182. '#camGirls .handle { cursor:pointer; display:inline; top:2px; left:2px; position:relative; float:left; z-index:99; }'+
  183. '#camControls { border:1px solid #CCC; margin:2px; padding:3px; }'+
  184. '#camControls .active { border:1px solid black; background:#fff; color:#dc5500; }'+
  185. '</style>';
  186. $('body').append(css);
  187.  
  188.  
  189. self.getSaved();
  190. self.fixRefresh();
  191. self.updateLayout();
  192.  
  193. $(".sub-nav li").click(function(){
  194. var page = location.href;
  195. if (page.indexOf('#') >- 1)
  196. page = location.href.split("#")[0];
  197. var target = location.origin + $(this).find('a').attr('href');
  198. if (page != target){
  199. return true;
  200. }
  201. else {
  202. $("#main .content").show();
  203. $("#main #camGirls").css({"visibility":"hidden","height":"0px"});
  204. $(".sub-nav li").removeClass("active");
  205. $(this).addClass("active");
  206. location.hash = "#tab"
  207. return false;
  208. }
  209. });
  210.  
  211. var li = $("<li>").html("<a href='javascript:viewer.show();'>MULTI-CAM</a>");
  212.  
  213. $(".sub-nav").append(li);
  214. }
  215.  
  216. this.fixRefresh = function(){
  217. jQuery( "li.cams" ).live("click", function() {
  218. viewer.add_girl($(this).parents('li').find('a').attr('href'),this);
  219. }).css("cursor","pointer").attr("title","Add girl to MULTI-CAM");
  220. }
  221.  
  222. this.show = function(){
  223. $(".sub-nav li").removeClass("active");
  224. $(".sub-nav li:last").addClass("active");
  225. $("#main .content").hide();
  226. $("#main #camGirls").css({"visibility":"","height":"auto"});
  227. location.hash = "#live"
  228. if (self.loaded == false){
  229. self.loaded = true;
  230. self.updateLayout();
  231. }
  232. }
  233.  
  234. this.addTop12 = function(){
  235. $(".list > li:lt(12)").each(function(){
  236. self.add_girl($(this).find('a').attr('href'));
  237. });
  238. self.updateLayout();
  239. }
  240.  
  241. this.add = function(){
  242. viewer.girls.push(new Girl($('#camGirlUsername').val()));
  243. $("#camGirlUsername").val("");
  244. self.updateLayout()
  245. }
  246.  
  247. this.add_girl = function(username,obj){
  248. self.girls.push(new Girl(username));
  249. $(obj).html("Girl added to MULTI-CAM");
  250. self.loaded = false;
  251. }
  252.  
  253. this.remove = function(username,elem){
  254. $.each(self.girls, function(i,o){
  255. if (typeof o != "undefined" && o.username.toLowerCase().indexOf(username.toLowerCase()) >-1 ){
  256. self.girls.splice(i,1);
  257. $(elem).parent().remove();
  258. }
  259. });
  260. self.updateLayout();
  261. }
  262.  
  263. this.clearEmptyCams = function(){
  264. $("#girls_list iframe").each(function(){
  265. var username = $(this).parents("li")[0].id;
  266. //need to find a window variable that'll indicate when the flash object is there and it's offline
  267. if ($(this.contentWindow.document).find('#movie').length == 0)
  268. self.remove(username);
  269. });
  270. }
  271.  
  272. this.removeAll = function(){
  273. self.girls = [];
  274. self.updateLayout();
  275. }
  276.  
  277. this.updateLayout = function(){
  278. if ($("#camGirls:visible").length > 0) {
  279. $.each(self.girls, function(){
  280. if ($("li#"+this.username).length == 0)
  281. $("#girls_list").append(toHtml(this,list_template));
  282. });
  283. $("#girls_list li").each(function(){
  284. var user = this.id;
  285. var isIncluded = $.map(viewer.girls,function(o,i){
  286. if (o.username == user){
  287. return true;
  288. }
  289. }).length > 0;
  290. if (isIncluded == false)
  291. $(this).remove();
  292. });
  293. self.layout(self.layout_id);
  294. }
  295. }
  296.  
  297. this.getSaved = function(){
  298. gm.get_layout();
  299. gm.get_girls();
  300. }
  301.  
  302. this.save = function(){
  303. gm.set_girls();
  304. gm.set_layout();
  305. alert("Saved");
  306. }
  307.  
  308. this.layout = function(id){
  309. self.layout_id = id;
  310. if (id == 1){
  311. var columWidth = 500;
  312. var columnHeight = 470;
  313. var top = 0;
  314. }
  315. else if (id == 2){
  316. var minWidth = 400;
  317. var columns = Math.floor($(window).width() / minWidth);
  318. var columWidth = Math.floor($(window).width() / columns) - 5;
  319. var columnHeight = 375;
  320. var top = -66;
  321. }
  322. else if (id == 3){
  323. var columWidth = 1030;
  324. var columnHeight = 544;
  325. var top = 0;
  326. }
  327. $("#camControls input").removeClass('active')
  328. $("#layout_" + id).addClass('active')
  329. $("#camGirls li").width(columWidth);
  330. $("#camGirls li").height(columnHeight);
  331. $("#camGirls iframe").css({ top: top+"px" });
  332. }
  333.  
  334. });
  335. $.each(exports.split(","),function(i,o){
  336. window[o] = eval(o);
  337. });
  338. window.viewer.init();
  339. });
  340. }
  341. }
  342.  
  343. var script = document.createElement("script");
  344. script.textContent = "(" + main.toString() + ")();";
  345. document.body.appendChild(script);
  346. });