Förbättrar Chaturbate genom att lägga till flera nya funktioner.
// ==UserScript== // @name Chaturbate Enhancer // @name:de Chaturbate Enhancer // @name:es Chaturbate Enhancer // @name:es-CO Chaturbate Enhancer // @name:it Chaturbate Enhancer // @name:fr Chaturbate Enhancer // @name:fr-CA Chaturbate Enhancer // @name:ru Chaturbate Enhancer // @name:tr Chaturbate Enhancer // @name:ro Chaturbate Enhancer // @name:no Chaturbate Enhancer // @name:nl Chaturbate Enhancer // @name:pl Chaturbate Enhancer // @name:ja Chaturbate Enhancer // @name:el Chaturbate Enhancer // @name:hu Chaturbate Enhancer // @name:fi Chaturbate Enhancer // @name:ar Chaturbate Enhancer // @name:hi Chaturbate Enhancer // @name:id Chaturbate Enhancer // @name:ko Chaturbate Enhancer // @name:pt-PT Chaturbate Enhancer // @name:pt-BR Chaturbate Enhancer // @name:zh Chaturbate Enhancer // @name:zh-CN Chaturbate Enhancer // @name:zh-TW Chaturbate Enhancer // @name:cs Chaturbate Enhancer // @name:sk Chaturbate Enhancer // @name:sl Chaturbate Enhancer // @name:sv Chaturbate Enhancer // @name:sr Chaturbate Enhancer // @name:af Chaturbate Enhancer // @name:sq Chaturbate Enhancer // @name:hy Chaturbate Enhancer // @name:be Chaturbate Enhancer // @name:bg Chaturbate Enhancer // @name:da Chaturbate Enhancer // @name:et Chaturbate Enhancer // @name:he Chaturbate Enhancer // @name:hr Chaturbate Enhancer // @name:fa Chaturbate Enhancer // @name:ur Chaturbate Enhancer // @name:bn Chaturbate Enhancer // @name:th Chaturbate Enhancer // @name:eo Chaturbate Enhancer // @name:ug Chaturbate Enhancer // @name:vi 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:fr-CA 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:zh-CN 通过添加多个新功能来增强 Chaturbate。 // @description:zh-TW 通过添加多个新功能来增强 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 উন্নত করে। // @description:th ปรับปรุง Chaturbate ด้วยการเพิ่มคุณสมบัติใหม่หลายอย่าง // @description:eo Plibonigas Chaturbate aldonante plurajn novajn funkciojn. // @description:ug كۆپ خىل يېڭى ئىقتىدارلارنى قوشۇش ئارقىلىق Chaturbate نى كۈچەيتىدۇ. // @description:vi Cải thiện Chaturbate bằng cách thêm nhiều tính năng mới. // @version 7.2.1 // @author improper.dev // @license CC-BY-ND-4.0; https://creativecommons.org/licenses/by-nd/4.0/legalcode // @copyright improper.dev (https://improper.dev/) // @namespace https://improper.dev/ // @homepage https://cb-enh.improper.dev/ // @supportURL https://sleazyfork.org/en/scripts/441079-chaturbate-enhancer/feedback // @contributionURL https://cb-enh.improper.dev/contribute // @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/* // @exclude https://chaturbate.com/security/* // @exclude https://*.chaturbate.com/security/* // @connect cb-enh.improper.dev // @connect cb-enh-api.improper.dev // @connect cb-enh-api2.improper.dev // @connect cb-enh-thumb.improper.dev // @grant GM_addStyle // @grant GM_addElement // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_setClipboard // @grant GM_download // @run-at document-start // @antifeature referral-link // @noframes // ==/UserScript== /* Chaturbate™ Enhancer v7.2.1 © improper.dev Please do not share modified version of this script without permission. We have spent significant time and effort on developing this script. Instead, you are welcome to contribute to the main script code. You can submit code patches, bug reports and ideas at https://sleazyfork.org/en/scripts/441079-chaturbate-enhancer/feedback or via e-mail: [email protected] You will be credited for your contributions. Thank you! */ /* Attribution-NoDerivatives 4.0 International ======================================================================= Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an "as-is" basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC- licensed material, or material used under an exception or limitation to copyright. More considerations for licensors: wiki.creativecommons.org/Considerations_for_licensors Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor's permission is not necessary for any reason--for example, because of any applicable exception or limitation to copyright--then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public: wiki.creativecommons.org/Considerations_for_licensees ======================================================================= Creative Commons Attribution-NoDerivatives 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NoDerivatives 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 -- Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. c. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. d. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. e. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. f. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. g. Licensor means the individual(s) or entity(ies) granting rights under this Public License. h. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. i. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. j. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 -- Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or in part; and b. produce and reproduce, but not Share, Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. 5. Downstream recipients. a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. b. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. Section 3 -- License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material, You must: a. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. For the avoidance of doubt, You do not have permission under this Public License to Share Adapted Material. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. Section 4 -- Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database, provided You do not Share Adapted Material; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 -- Disclaimer of Warranties and Limitation of Liability. a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 -- Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 -- Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 -- Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. ======================================================================= Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” The text of the Creative Commons public licenses is dedicated to the public domain under the CC0 Public Domain Dedication. Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. */ (function() { 'use strict'; function addStyle(style) { if(document.head === null) { if(typeof GM_addStyle !== 'undefined') { return GM_addStyle(style); } return; } const elStyle = document.createElement('style'); elStyle.textContent = style; document.head.appendChild(elStyle); return elStyle; } function addElement(parent_node, tag_name, attributes) { if(typeof GM_addElement !== 'undefined') { return GM_addElement(parent_node, tag_name, attributes) } } function xmlhttpRequest(details) { if(typeof GM_xmlhttpRequest !== 'undefined') { return GM_xmlhttpRequest(details); } } function registerMenuCommand(name, callback, accessKey) { if(typeof GM_registerMenuCommand !== 'undefined') { return GM_registerMenuCommand(name, callback, accessKey); } } function unregisterMenuCommand(menuCmdId) { if(typeof GM_unregisterMenuCommand !== 'undefined') { return GM_unregisterMenuCommand(menuCmdId); } } function setClipboard(data, info) { if(typeof GM_setClipboard !== 'undefined') { return GM_setClipboard(data, info); } } function download(data) { if(typeof GM_download !== 'undefined') { GM_download(data); return; } const elLink = document.createElement('a'); elLink.download = data['name']; elLink.href = data['url']; elLink.click(); if('onloadend' in data) { data['onloadend'](); } } const gVersion = '7.2.1'; let gIntvWaitVideo = null; let gIntvUpdateVideoTime = null; let gIntvWaitMoreRooms = null; let gIntvAcceptRules = null; let gIntvAddVideoControlsBtn = null; let gIntvWaitBuyBox = null; let gIntvUpdateFollowedList = null; let gIntvCheckIfRoomIsOnline = null; let gIntvPatchVideo = null; let gIntvPatchHLS = null; const BEST_VIDEO_CODEC = 'av01.0.12M.08'; let gCheckedForAccel = false; let gCanAccel = null; let gSettings = {}; let gLocales = {}; let gIntvCurrentHover = null; let gLastLoadedThumbReqTime = 0; let gCurrentBroadcaster = null; let gCurrentRoomIsInaccessible = false; let gVideoControlsModalShown = false; let gCapturingScreenshot = false; let gRecording = false; let gRecordingStream = null; let gRecordingCanceledByUser = false; let gRecordingExitWarn = false; let gIntvRecordingUpdate = null; let gRecordAudio = true; let gRecordVideo = true; let gIsInMultiView = false; let gUnmuteCurrentVideoOnFocus = false; let gOpenRoomsInNewTab = false; let gWasMutedBySetting = false; let gReduceQualityOfInactiveStreams = true; const REGION_LINKS = { 0: '/asian-cams/', 1: '/euro-russian-cams/', 2: '/north-american-cams/', 3: '/south-american-cams/', 4: '/other-region-cams/' }; const gMoreSVG = '<svg xmlns="http://www.w3.org/2000/svg" height="20" viewBox="0 -960 960 960" width="20"><path d="M479.788-192Q450-192 429-213.212q-21-21.213-21-51Q408-294 429.212-315q21.213-21 51-21Q510-336 531-314.788q21 21.213 21 51Q552-234 530.788-213q-21.213 21-51 21Zm0-216Q450-408 429-429.212q-21-21.213-21-51Q408-510 429.212-531q21.213-21 51-21Q510-552 531-530.788q21 21.213 21 51Q552-450 530.788-429q-21.213 21-51 21Zm0-216Q450-624 429-645.212q-21-21.213-21-51Q408-726 429.212-747q21.213-21 51-21Q510-768 531-746.788q21 21.213 21 51Q552-666 530.788-645q-21.213 21-51 21Z"/></svg>'; const gSVGPiPBtn = '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="white"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm0-80h640v-480H160v480Zm0 0v-480 480Zm280-200h320v-240H440v240Zm80-80v-80h160v80H520Z"/></svg>'; const gSVGCameraBtn = '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M480-260q75 0 127.5-52.5T660-440q0-75-52.5-127.5T480-620q-75 0-127.5 52.5T300-440q0 75 52.5 127.5T480-260Zm0-80q-42 0-71-29t-29-71q0-42 29-71t71-29q42 0 71 29t29 71q0 42-29 71t-71 29ZM160-120q-33 0-56.5-23.5T80-200v-480q0-33 23.5-56.5T160-760h126l74-80h240l74 80h126q33 0 56.5 23.5T880-680v480q0 33-23.5 56.5T800-120H160Zm0-80h640v-480H638l-73-80H395l-73 80H160v480Zm320-240Z"/></svg>'; const _defineProperty = Object.defineProperty; unsafeWindow.Object.defineProperty = function(obj, prop, descriptor) { if(prop === 'hide_entrance_terms') { retur; } return _defineProperty.call(Object, obj, prop, descriptor); }; function getCookie(name) { const nameEQ = encodeURIComponent(name) + "="; const ca = document.cookie.split(';'); for(let i = 0; i < ca.length; i++) { let c = ca[i]; while(c.charAt(0) === ' ') { c = c.substring(1, c.length); } if(c.indexOf(nameEQ) === 0) { return decodeURIComponent(c.substring(nameEQ.length, c.length)); } } return null; } function doUserPrefsDarkMode() { return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; } function isDarkModeSet() { return document.body && document.body.classList.contains('darkmode'); } function enableDarkMode() { if(!doUserPrefsDarkMode()) { return; } makeSureRuns(() => { if(document.body) { document.body.classList.add('darkmode'); } }); if(getCookie('theme_name') === null) { document.cookie = 'theme_name=darkmode; expires=Sun, 1 Jan 9999 00:00:00 UTC; path=/'; document.cookie = 'theme_name=darkmode; expires=Sun, 1 Jan 9999 00:00:00 UTC; path=/; domain=.chaturbate.com'; } } function isMissingDarkModePage() { const missingDarkModePages = [ '/my_collection/', '/fanclub/join/', '/supporter/upgrade/', '/terms/', '/privacy/', '/2257/', '/law_enforcement/', '/billingsupport/', '/auth/login/', '/accounts/register/' ]; let res = false; missingDarkModePages.forEach((v) => { if(window.location.pathname.startsWith(v)) { res = true; return false; } return true; }); return res; } if(doUserPrefsDarkMode()) { enableDarkMode(); const bodyObserver = new MutationObserver(() => { if(document.body) { bodyObserver.disconnect(); document.body.classList.add('darkmode'); if(isMissingDarkModePage()) { document.body.classList.add('darkmode-cenhf'); } } }); bodyObserver.observe(document.documentElement, { childList: true, subtree: true }); } if(getCookie('agreeterms') !== '1') { document.cookie = 'agreeterms=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=/; domain=.chaturbate.com'; window.location.reload(); } function isMultiViewPage() { return window.location.pathname === '/multicam/'; } function isAG() { return getCookie('AG_Key') !== null; } function define(obj, prop, value) { Object.defineProperty(obj, prop, { get() { return value; } }); } function patchXHR(xhr, statusCode, responseText) { define(xhr, 'status', statusCode); define(xhr, 'statusText', statusCode === 200 ? 'OK' : 'Error'); define(xhr, 'responseText', responseText); define(xhr, 'response', responseText); } function createRoomData(data, username) { return JSON.stringify({ room_pass: '', last_pass: '', chat_rules: '', is_supporter: false, needs_supporter_to_pm: false, apps_running: '', allow_private_shows: false, private_show_price: 0, private_min_minutes: 0, allow_show_recordings: false, allow_anonymous_tipping: false, spy_private_show_price: 0, private_show_id: '', low_satisfaction_score: false, is_age_verified: false, hidden_message: '', following: false, follow_notification_frequency: '', is_moderator: false, token_balance: 0, has_studio: false, is_mobile: false, ignored_emoticons: [], hide_satisfaction_score: false, tips_in_past_24_hours: 0, last_vote_in_past_90_days_down: false, dismissible_messages: [], show_mobile_site_banner_link: false, fan_club_is_member: false, performer_has_fanclub: false, fan_club_paid_with_tokens: false, satisfaction_score: { up_votes: 0, down_votes: 0, percent: 0 }, tfa_enabled: false, chat_settings: { font_color: '', font_family: '', font_size: '', show_emoticons: true, emoticon_autocomplete_delay: 0, highest_token_color: '', sort_users_key: 'alphabetical', mod_expire: 0, room_entry_for: 'all', room_leave_for: 'all', c2c_notify_limit: 0, silence_broadcasters: '', ignored_users: '', allowed_chat: '', tip_volume: 50, collapse_notices: false }, asp_auth_url: '', asp_app_directory_v3_enabled: false, browser_id: '', userlist_color: '', active_password: false, is_tip_menu_enabled: false, broadcaster_username: data.broadcaster_username || username, room_uid: data.room_uid || '', room_title: data.room_title || '', room_status: data.room_status || 'offline', viewer_username: data.viewer_username || '', broadcaster_gender: data.broadcaster_gender || '', hls_source: data.hls_source || '', is_widescreen: !!data.is_widescreen, is_portrait: !!data.is_portrait, edge_region: data.edge_region || '', exploring_hashtag: data.exploring_hashtag || '', source_name: data.source_name || '', age: data.age || 0, num_viewers: data.num_viewers || 0, cmaf_edge: !!data.cmaf_edge }); } function clearIntervalEx(intv) { if(typeof intv !== 'undefined' && intv) { clearInterval(intv); } return null; } function setIntervalRunFirst(callback, timeout) { if(callback() === false) { return null; } return setInterval(callback, timeout); } const gIsAG = isAG(); const gAChat = isMultiViewPage(); if(gIsAG) { let intvWaitForDataInit1; intvWaitForDataInit1 = setIntervalRunFirst(() => { if(typeof unsafeWindow.$reactAppContext === 'undefined') { return; } clearIntervalEx(intvWaitForDataInit1); Object.defineProperty(unsafeWindow.$reactAppContext, 'user_age_gated', { get() { return false; }, set(v) { return true; } }); return false; }, 10); let intvWaitForDataInit2; intvWaitForDataInit2 = setIntervalRunFirst(() => { if(typeof unsafeWindow.tsInstance === 'undefined') { return; } clearIntervalEx(intvWaitForDataInit2); Object.defineProperty(unsafeWindow.tsInstance.context, 'isAgeGated', { get() { return false; }, set(v) { return true; } }); return false; }, 10); const next = new URLSearchParams(window.location.search).get('next'); if(next && /^\/(?!\/)/.test(next)) { history.replaceState({}, '', next); window.dispatchEvent(new PopStateEvent('popstate', { state: {} })); } addStyle(`.msg-list-wrapper-split {line-height: 16pt !important;}`); } const _xhrOpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url) { if(gAChat && typeof url === 'string' && url.includes('/push_service/auth/')) { this._hook_block = true; } else if(gIsAG && typeof url === 'string' && url.includes('/api/chatvideocontext/')) { this._hook_ag = true; const m = url.match(/chatvideocontext\/([^/]+)/); this._username = m[1]; } return _xhrOpen.apply(this, arguments); }; const _xhrSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function() { if(this._hook_block) { return; } if(!this._hook_ag) { return _xhrSend.apply(this, arguments); } const xhr = this; const req = new XMLHttpRequest(); req.addEventListener('load', function() { let data; try { data = JSON.parse(this.responseText); } catch(e) { return; } patchXHR(xhr, 200, createRoomData(data, xhr._username)); xhr.dispatchEvent(new Event('load')); xhr.dispatchEvent(new Event('loadend')); }); req.timeout = 60*1*1000; req.open('GET', '/api/livecampreviewcontext/' + xhr._username + '/'); req.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); req.withCredentials = true; req.send(); }; if(isMultiViewPage()) { addStyle(`.content, #theatermode-root {display: none !important;}`); } const gMultiGridColumnCount = 60; const style = `.photoVideoDetailSection img {filter: unset !important;}.userUpload div {background: none !important;}.psContainer .lockOverlayBg, .smContainer .lockOverlayBg {display: none !important;}a[data-testid="photo-video-item"] > img[data-testid="lock-icon"] {display: none !important;}a[data-testid="photo-video-item"] > div:not(.link) {background: none !important;}.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;display: none;}.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-offline-snapshot {display: block;border-radius: 4px;margin-top: 10px;}.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;}tr:not(.smContainer) .contentText .previewBorder, div[data-testid="photoVideoPreview"] {width: 190px !important;height: 135px !important;}div[data-testid="photoVideoPreview"] .previewBorder {width: 100%;height: 100%;}a[data-testid="photo-video-item"] .tokenText {top: unset !important;bottom: 11px !important;right: -5px !important;}a[data-testid="photo-video-item"] img[src$="/tsdefaultassets/video.svg"] {top: 4px !important;right: 4px !important;}a[data-testid="photo-video-item"] img[src$="/tsdefaultassets/no-audio.svg"] {top: 4px !important;right: 26px !important;}body.cb-enh-nsc div[data-testid="bio-tab-about-me-value"] a[href^="/external_link/?url="],body.cb-enh-nsc div[data-testid="bio-tab-wish-list-value"] a[href^="/external_link/?url="] {display: none !important;}body.cb-enh-nsc .BioContents tr.smContainer:has(td[data-testid="bio-tab-social-media-value"]) {display: none !important;}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, tr:not(.smContainer):not(.psContainer) .contentText span {position: unset !important;}div[data-testid="bio-tab-about-me-value"] a[href*="/accounts/register/"],div[data-testid="bio-tab-about-me-value"] a:not([href^="/external_link/"])[href*="campaign="],div[data-testid="bio-tab-about-me-value"] a:not([href^="/external_link/"])[href*="/in/"],div[data-testid="bio-tab-about-me-value"] a[href^="/external_link/?url="][href*="%2FStatus.aspx%3F"],div[data-testid="bio-tab-about-me-value"] a[href^="/external_link/?url="][href*="cbhours.com%2F"],div[data-testid="bio-tab-about-me-value"] a[href^="/external_link/?url="][href*="cbexplorer.com%2F"],div[data-testid="bio-tab-wish-list-value"] a[href*="/accounts/register/"],div[data-testid="bio-tab-wish-list-value"] a:not([href^="/external_link/"])[href*="campaign="],div[data-testid="bio-tab-wish-list-value"] a:not([href^="/external_link/"])[href*="/in/"],div[data-testid="bio-tab-wish-list-value"] a[href^="/external_link/?url="][href*="%2FStatus.aspx%3F"],div[data-testid="bio-tab-wish-list-value"] a[href^="/external_link/?url="][href*="cbhours.com%2F"],div[data-testid="bio-tab-wish-list-value"] a[href^="/external_link/?url="][href*="cbexplorer.com%2F"] {display: none !important;}tr:not(.smContainer):not(.psContainer) .contentText *:not(.fi) {background: unset !important;}tr:not(.smContainer):not(.psContainer) .contentText * {cursor: auto !important;}tr:not(.smContainer):not(.psContainer) .contentText a {cursor: pointer !important;}tr:not(.smContainer):not(.psContainer) .contentText a * {cursor: pointer !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;border-radius: 4px;}.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-schedule-frame {width: 100%;height: 350px;border: 0;}.cb-enh-chat-frame {width: 100%;max-width: 1400px;height: 700px;border: 0;border-radius: 4px;}#cb-enh-inac-load-chat {cursor: pointer;}.noselect {-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-video-bar-btn {--bg: #880471;height: 15px;width: auto;position: relative;overflow: hidden;-webkit-tap-highlight-color: transparent;font-family: UbuntuMedium, Helvetica, Arial, sans-serif;font-size: 12px;padding: 3px 8px 2px;top: -4px;right: 1px;float: right;border-radius: 3px;cursor: pointer;margin-right: 5px;background-color: var(--bg);color: white;text-transform: uppercase; border-bottom-width: 0.8px; border-left-width: 0.8px; border-right-width: 0.8px; border-top-width: 0.8px; border: 1px solid var(--bg);}#cb-enh-video-controls-record {--bg: #090;}#cb-enh-video-controls-record.cb-enh-active {--bg: #ff0000;}.cb-enh-video-bar-btn-hide {--bg: #4f4f4f;}.cb-enh-video-bar-btn-hide.cb-enh-active {--bg: #890000;}.tabBar .fanclubButton, .tabBar .followButton, .tabBar .unfollowButton { line-height: normal !important;}.cb-enh-tab-bar-modal {width: 500px;border-width: 1px;position: absolute;border-style: solid;border-radius: 4px;font-size: 14px;padding: 8px 8px 8px 8px;display: none;z-index: 15;line-height: 22px;box-shadow: rgba(0, 0, 0, 0.08) 0px 4px 16px;background-color: #fdfdfd;border: 1px solid #acacac;}.darkmode .cb-enh-tab-bar-modal {background-color: #19222c;border-color: #003061;color: #f0f0f0;}.cb-enh-arrow {position: absolute;width: 0;height: 0;border-left: 10px solid transparent;border-right: 10px solid transparent;}.cb-enh-arrow-down {border-top: 10px solid #0554a3;}.cb-enh-arrow-up {border-bottom: 10px solid #0554a3;}.videoPlayerDiv:not([style*='height: 100%; width: 100%;']) video.cb-enh-video-transformed {width: calc(100% - 10px) !important;}.vjs-fullscreen video.cb-enh-video-transformed {width: calc(100% - 10px) !important;}.videoPlayerDiv.cb-enh-video-rotated > canvas {display: none !important;}.videoPlayerDiv.cb-enh-video-rotated {background: none !important;}.cb-enh-video-controls-modal-btns {margin-top: 5px;}.cb-enh-vid-control-slider {width: 50%;}.entrance-terms--shown {position: inherit !important;top: inherit !important;left: inherit !important;right: inherit !important;bottom: inherit !important;overflow: inherit !important;background-color: #fff;visibility: inherit !important;}#entrance_terms_overlay {display: none !important;}#cb-enh-acc-info {width: 500px;height: 69px;box-sizing: border-box;font-size: 15px;overflow: hidden;display: inline-block;vertical-align: top;margin: 0px;float: right;color: #292929;}.darkmode #cb-enh-acc-info {color: #f2f2f2;}.cb-enh-acc-info-outer {position: relative;height: 100%;}.cb-enh-acc-info-inner {margin: 0;position: absolute;top: 50%;transform: translateY(-50%);right: 0%;margin-right: 10px;line-height: 15px;cursor: pointer;}.cb-enh-acc-info-small-text {font-size: 13px;line-height: 13px;}#cb-enh-multi-support .cb-enh-acc-info-small-text {margin-top: 4px;font-size: 15px;line-height: 17px;}.blurred-login-overlay > div > span, .blurred-login-overlay > div > hr {display: none;}.thumbnail_label {pointer-events: none;text-transform: uppercase;}.RoomCardThumbnail__labelContainer {pointer-events: none;}div[data-testid="quality-option"] {-webkit-touch-callout: none;-webkit-user-select: none;-khtml-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;}div.slider[aria-label="Volume Slider"] div {-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-theater-controls-area {position: absolute;width: 100%;heigth: 50px;}.vjs-menu-button-popup .vjs-menu .vjs-menu-content {max-height: 20em !important;}div.vjs-menu-button.vjs-menu-button-popup.vjs-control.vjs-button .vjs-menu-content {border-radius: 8px 8px 0 0;}#TheaterModePlayer #volume-high, #TheaterModePlayer #volume-mute, .theater-video-controls {-webkit-user-drag: none;-webkit-app-region: no-drag;user-drag: none;app-region: no-drag;-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-grid-size-selector-img {float: right;position: relative;margin: 0;display: block;width: 30px;margin-left: 0px;cursor: pointer;}.cb-enh-grid-size-selector-img:not(.cb-enh-grid-size-selector-more-img) {display: none;}.darkmode .cb-enh-grid-size-selector-img path {fill: #002e43;}.darkmode .cb-enh-grid-size-selector-img path {fill: #68b4ef;}.main-content-wrapper:has(.top-section:not(.roomPage)) .cb-enh-grid-size-selector-img:not(.cb-enh-grid-size-selector-more-img) {display: block;}#cb-enh-multi-size-selector .cb-enh-grid-size-selector-img {display: block;}.MoreRooms .list .roomCard a img {width: 100%;height: 100%;}.cb-enh-grid-size-selector-more-img {margin-top: -20px;margin-right: -10px !important;margin-left: 8px;}.cb-enh-add-cam-icon, .cb-enh-room-more-icon {float: right;cursor: pointer;}.cb-enh-room-preview-icon {display: block;float: left;cursor: pointer;position: absolute;margin-top: 1px;margin-left: 1px;top: 0;left: 0;width: 23.25px;height: 23.25px;filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 1));z-index: 1;opacity: 0;}.RoomCard:hover[data-show-big-preview-icon] .cb-enh-room-preview-icon {opacity: 0.95;}.cb-enh-room-more-icon {margin-right: 10px;}.cb-enh-add-cam-icon {margin-right: 2px;}.sub-info li.cams {width: 100% !important;}.cb-enh-room-more-icon {top: -1px;position: relative;}.darkmode .cb-enh-add-cam-icon svg path, .darkmode #cb-enh-multi-tip-add-icon svg path, .cb-enh-multi-fullscreen #cb-enh-multi-tip-add-icon svg path,.darkmode .cb-enh-room-more-icon svg path {fill: #fbfbfb;}.cb-enh-big-window {position: fixed;z-index: 99999999;top: 0;left: 0;width: 100%;display: none;justify-content: center;}.cb-enh-big-window a {text-decoration: underline;color: black;}.darkmode .cb-enh-big-window a {color: white;}.cb-enh-big-window-close-icon {cursor: pointer;position: absolute;top: 4px;right: 4px;}.cb-enh-big-window-close-icon svg path {fill: #e80000;}.cb-enh-big-window-inner {position: fixed;width: 100%;max-width: 800px;height: 565px;border-radius: 4px;border: 1px solid #acacac;background-color: #fbfbfb;padding: 10px;top: 17%;}.darkmode .cb-enh-big-window-inner {border: 1px solid #121820;background-color: #17202a;color: #f7f7f7;}.cb-enh-big-window-content {overflow: auto;width: 100%;height: 89%;font-size: 14px;line-height: 20px;}.cb-enh-big-window-header {font-size: 22px;font-weight: bold;margin-top: 5px;margin-bottom: 10px;}.cb-enh-big-window-subheader {font-size: 18px;font-weight: bold;}#cb-enh-page-overlay {display: none;position: fixed;top: 0;left: 0;width: 100%;height: 100%;background-color: #0000008a;z-index: 99999996;}body > #base.cb-enh-base-blurred {-moz-filter: blur(10px);-ms-filter: blur(10px);-o-filter: blur(10px);filter: blur(10px);transform: translateZ(0);pointer-events: none;}html.cb-enh-html-noscroll {overflow: hidden;}#nav {overflow: visible !important;}#cb-enh-toggle-settings {text-transform: uppercase;}#cb-enh-settings-window input[type="checkbox"], #cb-enh-settings-window label {cursor: pointer;}#cb-enh-settings-window input[type="checkbox"] {margin: 0;}#cb-enh-settings-window label {padding-left: 4px;}#cb-enh-settings-footer {position: absolute;bottom: 10px;}.cb-enh-st-blocklist-username-remove-icon {display: inline-block;cursor: pointer;vertical-align: middle;width: 17px;height: 17px;}.cb-enh-st-blocklist-username-remove-icon svg {width: 100%;height: 100%;}.cb-enh-st-blocklist-username-remove-icon svg path {fill: #e80000;}.cb-enh-st-blocklist-username {height: 20px;}#cb-enh-more-rooms-size-selectors {display: none;position: relative;top: 24px;right: 20px;}#roomTabs:has(.MoreRooms[style*="display: block;"]) #cb-enh-more-rooms-size-selectors {display: block;}#cb-enh-notification {display: none;position: fixed;z-index: 99999995;bottom: 10px;width: 100%;justify-content: center;pointer-events: none;}#cb-enh-notification-inner {width: 100%;max-width: 800px;height: 35px;border-radius: 4px;border: 1px solid #acacac;background-color: #fbfbfb;padding: 8px;pointer-events: all;font-size: 16px;opacity: 0.95;}.darkmode #cb-enh-notification-inner {border: 1px solid #121820;background-color: #0d1318;color: #f7f7f7;}.cb-enh-room-more-menu {display: none; border-radius: 2px; position: absolute;width: 120px;height: 26px; font-size: 13px; border: 1px solid #acacac; background-color: #f0f1f1; color: black;z-index: 99999997;list-style: none;padding: 0;margin: 0;}.darkmode .cb-enh-room-more-menu {border: 1px solid #2d3e50;background-color: #202b38;color: #fbfbfb;}.cb-enh-room-more-menu li {cursor: pointer;padding: 4px;}.cb-enh-room-more-menu li:hover {background-color: #e4e4e4;}.darkmode .cb-enh-room-more-menu li:hover {background-color: #101f30;}.darkmode .roomElementAnchor.isHighlighted {background-color: #193655 !important;}.darkmode .roomElementAnchor:hover {color: #00b6ff !important;}.darkmode .roomCard .sub-info {color: #a1a1a1 !important;}#cb-enh-multi-support, .cb-enh-standalone-support-block {clear: both;margin: 10px;margin-top: 15px;font-size: 18px;color: white;border: 1px solid #496b91;padding: 5px;border-radius: 4px;background-color: #003168;max-width: 750px;line-height: 21px;cursor: pointer;transition: 0.2s background-color;}.darkmode #cb-enh-multi-support, .darkmode .cb-enh-standalone-support-block {background-color: #012247;}#cb-enh-multi-support:hover, .cb-enh-standalone-support-block:hover {background-color: #003979;}#cb-enh-settings-support-block-outer {display: flex;position: fixed;z-index: 99999998;top: 0;left: 0;width: 100%;display: flex;justify-content: center;}#cb-enh-settings-support-block-outer.cb-enh-hidden {display: none;}#cb-enh-settings-support-block {width: 600px;position: fixed;bottom: 40px;z-index: 9999999;max-width: 800px;position: fixed;width: 100%;height: 85px;max-width: 800px;padding: 8px;}body.cb-enh-chat-hide-notices div[data-testid="chat-message"]:has(.roomNotice:not(.isTip):not(.titleChange):not(.bright-background)) {display: none !important;}body.cb-enh-chat-hide-subject-notices div[data-testid="chat-message"]:has(.roomNotice.titleChange) {display: none !important;}body.cb-enh-chat-hide-tips div[data-testid="chat-message"]:has(.isTip) {display: none !important;}body.cb-enh-chat-hide-greys div[data-testid="chat-message"]:has(.defaultUser) {display: none !important;}/*.cb-enh-multi-cam-img-wrap {display: none;}.cb-enh-multi-cam-wrap2 {background-color: unset !important;}*/#cb-enh-multi-cams {display: grid;grid-template-columns: repeat(` + gMultiGridColumnCount + `, 1fr);grid-template-rows: repeat(` + gMultiGridColumnCount + `, 1px);}.langs {padding-bottom: 10px !important;}.cb-enh-footer {padding-bottom: 10px;}#cb-enh-room-preview-video {position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;}.RoomCardThumbnail__image {position: relative;}#cb-enh-room-preview {display: none;position: fixed;width: 500px;height: 281.25px;bottom: 10px;right: 10px;background: black;border-radius: 12px;z-index: 9999;pointer-events: none;background-color: #f0f1f1;border: 1px solid #acacac;}.darkmode #cb-enh-room-preview {background-color: #202c39;border-color: #17202a;}#cb-enh-room-preview-preview {position: relative;}#cb-enh-room-preview video, #cb-enh-room-preview img {float: left;position: absolute;top: 0;left: 0;width: 100%;height: 100%;border-top-left-radius: 12px;border-top-right-radius: 12px;}#cb-enh-room-preview img {display: none;}#cb-enh-room-preview-info-top {border-bottom: 1px solid #acacac;height: 24px;}.darkmode #cb-enh-room-preview-info-top {border-bottom-color: #2d3e50;}#cb-enh-room-preview-username {font-size: 24px;color: #0c6a93;padding-bottom: 6px;float: left;}#cb-enh-room-preview-info {padding: 8px;}#cb-enh-room-preview-info-right {float: right;}#cb-enh-room-preview-info-age {font-size: 14px;}#cb-enh-room-preview-info-right > div, #cb-enh-room-preview-info-right .thumbnail_flag {float: left;}#cb-enh-room-preview-info-gender span {background-size: contain !important;width: 14px;height: 14px;display: inline-block;margin-left: 4px;}#cb-enh-room-preview-info-gender .genderf {background: url(https://web.static.mmcdn.com/images/ico-female.svg) no-repeat 100% 0;}#cb-enh-room-preview-info-gender .genderc {background: url(https://web.static.mmcdn.com/images/ico-couple.svg) no-repeat 100% 0;}#cb-enh-room-preview-info-gender .genderm {background: url(https://web.static.mmcdn.com/images/ico-male.svg) no-repeat 100% 0;}#cb-enh-room-preview-info-gender .genders {background: url(https://web.static.mmcdn.com/images/ico-trans.svg) no-repeat 100% 0;}.darkmode #cb-enh-room-preview-username {color: #68b5f0;}#cb-enh-room-preview-subject {clear: both;margin-top: 2px;font-size: 13px;overflow: hidden;max-height: 16px;}#cb-enh-room-preview-location {background: url(https://web.static.mmcdn.com/images/location.svg) no-repeat 0 50%;background-size: auto;padding-left: 18px;}.darkmode #cb-enh-room-preview-location {background: url(https://web.static.mmcdn.com/images/location_white.svg) no-repeat 0 50%;}#cb-enh-room-preview-caminfo { background: url(https://web.static.mmcdn.com/images/cam.svg) no-repeat 0 50%; background-size: auto;padding-left: 18px;}.darkmode #cb-enh-room-preview-caminfo { background: url(https://web.static.mmcdn.com/images/cam_white.svg) no-repeat 0 50%;}#cb-enh-pip svg {opacity: 0.8;-webkit-transform: scale(0.9);transform: scale(0.9);-webkit-transition-property: all;-webkit-transition-duration: 150ms;-webkit-transition-timing-function: ease;transition-property: all;transition-duration: 150ms;transition-timing-function: ease;}#cb-enh-pip:hover svg {opacity: 1.0;-webkit-transform: scale(1.1);transform: scale(1.1);}#cb-enh-pip:hover .video-controls-tooltip {opacity: 1 !important;visibility: visible !important;}div[data-testid="quality-option"], .video-controls-tooltip {-webkit-user-select: none;-khtml-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;}.thumbnail_flag {min-width: 16.01px;}#animate_thumbnails_form .tooltip {display: none !important;}.FollowRecommendedRoomlist .RoomCardGrid--oneRow {grid-template-rows: unset !important;grid-auto-rows: unset !important;grid-gap: 0.6em 0.75em !important;}.FollowRecommendedRoomlist {background-color: unset !important;border-top: 2px dashed #acacac !important;border-radius: unset !important;}.darkmode .FollowRecommendedRoomlist {border-color: #2d3e50 !important;}.FollowRecommendedRoomlist h2 {padding-top: 12px !important;padding-left: 1px !important;margin-bottom: 12px;}.darkmode .FollowRecommendedRoomlist h2 {color: #68b5f0 !important;}.followed_online_offline {padding: 0 !important;}.followed_online_offline p {margin: 0 !important;}.followed_online_offline .title[data-testid="online-filter-title"] {margin-left: 18px !important;}.followed_online_offline .title.bold {font-weight: bold !important;}#cb-enh-video-controls-modal-content input[type="checkbox"],#cb-enh-video-controls-modal-content input[type="button"] {cursor: pointer;}.cursor-pointer {cursor: pointer;}.FollowedDropdown {overflow-x: hidden !important;}div[data-testid="roomSubjectContainer"] {min-height: 34px !important;}.top-section {padding-top: 8px !important;padding-bottom: 0 !important;}.BaseRoomContents {margin-top: 0 !important;}body.cb-enh-small-margins .main-content-wrapper:has(.top-section.roomPage) {padding-left: 0 !important;padding-right: 0 !important;}body.cb-enh-small-margins .BaseRoomContents {margin-left: 0 !important;}body.cb-enh-small-margins #theatermode-root {margin-right: 0 !important;}body.cb-enh-small-margins:has(#theater-mode-icon[aria-label="Default View"]) #TheaterModeRoomContents {margin: 0 !important;padding: 0 !important;border: 0 !important;}body.cb-enh-small-margins:has(#theater-mode-icon[aria-label="Default View"]) #VideoPanel {min-width: calc(100% + 10px) !important;border: 0 !important;}body.cb-enh-gb-f:has(.GenderNav__button.active[data-testid="gender-nav-"]):not(:has(a#following.HeaderNavBar__link--active)) .RoomCard:has(span.genderf) {display: none !important;}body.cb-enh-gb-m:has(.GenderNav__button.active[data-testid="gender-nav-"]):not(:has(a#following.HeaderNavBar__link--active)) .RoomCard:has(span.genderm) {display: none !important;}body.cb-enh-gb-c:has(.GenderNav__button.active[data-testid="gender-nav-"]):not(:has(a#following.HeaderNavBar__link--active)) .RoomCard:has(span.genderc) {display: none !important;}body.cb-enh-gb-t:has(.GenderNav__button.active[data-testid="gender-nav-"]):not(:has(a#following.HeaderNavBar__link--active)) .RoomCard:has(span.genders) {display: none !important;}body.cb-enh-gb-f .FollowRecommendedRoomlist .RoomCard:has(span.genderf) {display: none !important;}body.cb-enh-gb-m .FollowRecommendedRoomlist .RoomCard:has(span.genderm) {display: none !important;}body.cb-enh-gb-c .FollowRecommendedRoomlist .RoomCard:has(span.genderc) {display: none !important;}body.cb-enh-gb-t .FollowRecommendedRoomlist .RoomCard:has(span.genders) {display: none !important;}.darkmode .FollowedDropdown__room-username--highlighted {background: #30455d !important;}.HeaderHomeLink {outline: none;}body.darkmode-cenhf form .fieldset_main th label {color: #ececec;}body.darkmode-cenhf #main .content_body a, body.darkmode-cenhf #main .form_body a {color: #f3f3f3 !important;}body.darkmode-cenhf #main a.medium_popup_link > div > p {color: #f3f3f3 !important;}body.darkmode-cenhf #main .content_body h1 {color: #ececec !important;}body.darkmode-cenhf #main h1 {color: #ececec !important;}.HeaderTopRow__search-bar > div.RichTooltip[role="tooltip"] {display: none !important;}#age_gate_overlay {display: none !important;}body.cb-enh-hvl #VideoPanel .cbLogo {display: none !important;}body.cb-enh-arl #ChatTabContents .rulesModal {visibility: hidden !important;}body.cb-enh-arl #ChatTabContents:has(.rulesModal) div[style^="position: fixed"] {visibility: hidden !important;}.cb-enh-tooltip {width: 250px;border-width: 1px;position: absolute;border-style: solid;border-radius: 4px;font-size: 14px;padding: 8px 8px 8px 8px;display: none;z-index: 15;line-height: 22px;box-shadow: rgba(0, 0, 0, 0.08) 0px 4px 16px;background-color: #fdfdfd;border: 1px solid #acacac;}.darkmode .cb-enh-tooltip {background-color: #19222c;border-color: #003061;color: white;}#cb-enh-record-warn-modal .cb-enh-big-window-inner {height: 70px;}#cb-enh-duration {display: none;position: relative;align-items: center;justify-content: center;min-width: 32px;user-select: none;pointer-events: auto;margin: 0px 6px;margin-top: 4px;color: white;}#cb-enh-duration > span {background-color: #00000094;border-radius: 4px;padding: 4px;}body.cb-enh-drt #cb-enh-duration {display: inline-flex;}.cb-enh-hidden {display: none;}#cb-enh-settings-window.cb-enh-show-rec-low-perf #cb-enh-settings-rec-low-perf,#cb-enh-settings-window.cb-enh-show-rec-low-perf label[for="cb-enh-settings-rec-low-perf"] {display: inline;}`; addStyle(style); const videoControlsContentHTML = `<b class="ce-loc" data-ce-loc="vid_controls">Video Controls</b>:<br><div><input type="checkbox" checked id="cb-enh-video-controls-modal-show-logo"></input><label for="cb-enh-video-controls-modal-show-logo" class="cursor-pointer ce-loc" data-ce-loc="show_site_logo">Show site logo</label><br></div><div><input type="checkbox" checked id="cb-enh-video-controls-modal-duration"></input><label for="cb-enh-video-controls-modal-duration" class="cursor-pointer ce-loc" data-ce-loc="set_duration">Show stream duration</label><br></div><div><input type="checkbox" id="cb-enh-video-controls-modal-mirror-vid"></input><label for="cb-enh-video-controls-modal-mirror-vid" class="cursor-pointer ce-loc" data-ce-loc="mirror_video">Mirror video</label><br></div><div><input type="checkbox" id="cb-enh-video-controls-modal-invert-vid"></input><label for="cb-enh-video-controls-modal-invert-vid" class="cursor-pointer ce-loc" data-ce-loc="invert_video">Invert video</label></div><div><input type="checkbox" id="cb-enh-video-controls-modal-rotate-left"></input><label for="cb-enh-video-controls-modal-rotate-left" class="cursor-pointer ce-loc" data-ce-loc="rotate_left">Rotate left</label></div><div><input type="checkbox" id="cb-enh-video-controls-modal-rotate-right"></input><label for="cb-enh-video-controls-modal-rotate-right" class="cursor-pointer ce-loc" data-ce-loc="rotate_right">Rotate right</label></div><div><input type="range" id="cb-enh-video-controls-modal-brightness" data-default="100" min="0" max="200" class="cb-enh-vid-control-slider"><label for="volume" for="cb-enh-video-controls-modal-brightness" class="ce-loc" data-ce-loc="brightness">Brightness</label></div><div><input type="range" id="cb-enh-video-controls-modal-contrast" data-default="100" min="0" max="200" class="cb-enh-vid-control-slider"><label for="volume" for="cb-enh-video-controls-modal-contrast" class="ce-loc" data-ce-loc="contrast">Contrast</label></div><div><input type="range" id="cb-enh-video-controls-modal-saturation" data-default="100" min="0" max="200" class="cb-enh-vid-control-slider"><label for="volume" for="cb-enh-video-controls-modal-saturation" class="ce-loc" data-ce-loc="saturation">Saturation</label></div><div><input type="range" id="cb-enh-video-controls-modal-sepia" data-default="0" value="0" min="0" max="100" class="cb-enh-vid-control-slider"><label for="volume" for="cb-enh-video-controls-modal-sepia" class="ce-loc" data-ce-loc="sepia">Sepia</label></div><div><input type="range" id="cb-enh-video-controls-modal-hue" data-default="0" value="0" min="0" max="360" class="cb-enh-vid-control-slider"><label for="volume" for="cb-enh-video-controls-modal-hue" class="ce-loc" data-ce-loc="hue">Hue</label></div><div><input type="range" id="cb-enh-video-controls-modal-blur" data-default="0" value="0" min="0" max="100" class="cb-enh-vid-control-slider"><label for="volume" for="cb-enh-video-controls-modal-blur" class="ce-loc" data-ce-loc="blur">Blur</label></div><div class="cb-enh-video-controls-modal-btns"><input type="button" id="cb-enh-video-controls-modal-reset" value="Reset" class="ce-loc" data-ce-loc="reset"></div>`; const settingsContentHTML = `<div class="cb-enh-big-window-inner"><div class="cb-enh-big-window-header">Chaturbate Enhancer <span class="ce-loc" data-ce-loc="settings">Settings</span></div><div class="cb-enh-big-window-close-icon">{{closeSVG}}</div><div class="cb-enh-big-window-content"><input type="checkbox" checked id="cb-enh-settings-preview-rooms"></input><label for="cb-enh-settings-preview-rooms" class="ce-loc" data-ce-loc="preview_rooms">Preview cams on hover</label><br><input type="checkbox" id="cb-enh-settings-preview-rooms-video"></input><label for="cb-enh-settings-preview-rooms-video" class="ce-loc" data-ce-loc="preview_rooms_video">Preview cams on hover as video</label><br><input type="checkbox" id="cb-enh-settings-mute-big-previews"></input><label for="cb-enh-settings-mute-big-previews" class="ce-loc" data-ce-loc="mute_big_previews">Mute large cam previews</label><br><input type="checkbox" checked id="cb-enh-settings-input-show-logo"></input><label for="cb-enh-settings-input-show-logo" class="ce-loc" data-ce-loc="set_show_site_logo">Show site logo in video player</label><br><input type="checkbox" id="cb-enh-settings-small-margins"></input><label for="cb-enh-settings-small-margins" class="ce-loc" data-ce-loc="small_page_margins">Small page margins</label><br><input type="checkbox" id="cb-enh-settings-blur-mute"></input><label for="cb-enh-settings-blur-mute" class="ce-loc" data-ce-loc="set_blur_mute">Mute inactive tabs</label><br><input type="checkbox" id="cb-enh-settings-duration"></input><label for="cb-enh-settings-duration" class="ce-loc" data-ce-loc="set_duration">Show stream duration</label><br><input type="checkbox" id="cb-enh-settings-inactive-load"></input><label for="cb-enh-settings-inactive-load" class="ce-loc" data-ce-loc="set_inactive_load">Load streams for inactive tabs</label><br><input type="checkbox" id="cb-enh-settings-inactive-quality"></input><label for="cb-enh-settings-inactive-quality" class="ce-loc" data-ce-loc="set_inactive_quality">Reduce stream quality in inactive tabs</label><br><input type="checkbox" id="cb-enh-settings-open-new-tab"></input><label for="cb-enh-settings-open-new-tab" class="ce-loc" data-ce-loc="set_open_new_tab">Open cams in new tabs</label><br><input type="checkbox" id="cb-enh-settings-no-socials"></input><label for="cb-enh-settings-no-socials" class="ce-loc" data-ce-loc="set_no_socials">Hide social media links</label><br><input type="checkbox" id="cb-enh-settings-auto-rules"></input><label for="cb-enh-settings-auto-rules" class="ce-loc" data-ce-loc="set_auto_rules">Chat: Auto-accept rules</label><br><input type="checkbox" id="cb-enh-settings-chat-hide-notices"></input><label for="cb-enh-settings-chat-hide-notices" class="ce-loc" data-ce-loc="chat_hide_notices">Chat: Hide notices</label><br><input type="checkbox" id="cb-enh-settings-chat-hide-subject-notices"></input><label for="cb-enh-settings-chat-hide-subject-notices" class="ce-loc" data-ce-loc="chat_hide_subject_notices">Chat: Hide subject change messages</label><br><input type="checkbox" id="cb-enh-settings-chat-hide-tips"></input><label for="cb-enh-settings-chat-hide-tips" class="ce-loc" data-ce-loc="chat_hide_tips">Chat: Hide tips</label><br><input type="checkbox" id="cb-enh-settings-chat-hide-greys"></input><label for="cb-enh-settings-chat-hide-greys" class="ce-loc" data-ce-loc="chat_hide_greys">Chat: Hide messages from grey users</label><br><input type="checkbox" id="cb-enh-settings-rec-exit-warn"></input><label for="cb-enh-settings-rec-exit-warn" class="ce-loc" data-ce-loc="rec_exit_warn">Recording: Warn before leaving while recording</label><br><input type="checkbox" id="cb-enh-settings-rec-low-perf" class="cb-enh-hidden"></input><label for="cb-enh-settings-rec-low-perf" class="cb-enh-hidden ce-loc" data-ce-loc="rec_low_perf">Recording: Low performance mode<br></label><input type="checkbox" id="cb-enh-settings-rec-no-audio"></input><label for="cb-enh-settings-rec-no-audio" class="ce-loc" data-ce-loc="rec_wo_audio">Recording: Record without audio</label><br><input type="checkbox" id="cb-enh-settings-rec-no-video"></input><label for="cb-enh-settings-rec-no-video" class="ce-loc" data-ce-loc="rec_wo_video">Recording: Record audio only</label><br><br><span class="ce-loc" data-ce-loc="set_show_these_genders">Show these genders in featured tab</span>:<input type="checkbox" checked class="cb-enh-settings-featuredg" id="cb-enh-settings-featuredg-f" data-gender="f"></input><label for="cb-enh-settings-featuredg-f" class="ce-loc" data-ce-loc="women">Women</label><input type="checkbox" checked class="cb-enh-settings-featuredg" id="cb-enh-settings-featuredg-m" data-gender="m"></input><label for="cb-enh-settings-featuredg-m" class="ce-loc" data-ce-loc="men">Men</label><input type="checkbox" checked class="cb-enh-settings-featuredg" id="cb-enh-settings-featuredg-c" data-gender="c"></input><label for="cb-enh-settings-featuredg-c" class="ce-loc" data-ce-loc="couples">Couples</label><input type="checkbox" checked class="cb-enh-settings-featuredg" id="cb-enh-settings-featuredg-t" data-gender="t"></input><label for="cb-enh-settings-featuredg-t" class="ce-loc" data-ce-loc="trans">Trans</label><br><br><div><span class="ce-loc cb-enh-big-window-subheader" data-ce-loc="hidden_rooms">Hidden cams</span></div><div class="ce-loc" data-ce-loc="set_blocklist_info">Hidden cams doesn't appear on the site. You can hide cams using dot menu on cam list pages or button below streams.</div>{{settingsContentBlocklistUsernameHTML}}</div><div id="cb-enh-settings-footer">Chaturbate™ Enhancer v` + gVersion + ` © <a href="https://improper.dev/" target="_blank" rel="noopener" referrerpolicy="origin">improper.dev</a></div></div>`; const gRecordWarnModalHTML = `<div class="cb-enh-big-window-inner"><div class="cb-enh-big-window-header"><span class="ce-loc" data-ce-loc="notice">Notice</span></div><div class="cb-enh-big-window-close-icon">{{closeSVG}}</div><div class="cb-enh-big-window-content"><span class="ce-loc" data-ce-loc="warn_rec_inprogress">Recording is in progress. Stop recording first.</span></div></div>`; const acre6 = 'a1lOjBM8aQ0duanUhryu'; const settingsContentExtraHTML = `<div id="cb-enh-settings-support-block-outer" class="cb-enh-hidden"><div class="cb-enh-standalone-support-block" id="cb-enh-settings-support-block"></div></div>`; const settingsContentBlocklistUsernameHTML = `<div class="cb-enh-st-blocklist-username"><div class="cb-enh-st-blocklist-username-remove-icon" data-username="{{username}}">{{closeSVG}}</div><span>{{username}}</span></div>`; function localize() { if(!gLocales) { return; } document.querySelectorAll('.ce-loc').forEach(function(el) { let v = el.dataset.ceLoc; if(gLocales[v]) { if(el.nodeName === 'INPUT') { el.value = gLocales[v]; } else { el.textContent = gLocales[v]; } } }); } function loadLocales(lang) { xmlhttpRequest({ method: 'GET', url: 'https://cb-enh.improper.dev/locale/' + lang + '.json?key=' + acre6, timeout: 60*1*1000, onload: function(resp) { let data; try { data = JSON.parse(resp.responseText); } catch(e) { return; } gLocales = data['locales']; localize(); } }); } function localizeNextTick() { runNextTick(localize); } function getLocale(name, failsafe) { if(gLocales && gLocales[name]) { return gLocales[name]; } if(failsafe) { return failsafe; } } function getSiteLang() { return document.documentElement.getAttribute('lang'); } function isFirefox() { return navigator.userAgent.toLowerCase().indexOf('firefox') !== -1; } const lang = getSiteLang(); if(lang !== 'en') { loadLocales(lang); } const gBlocklistRooms = []; function blocklistLoad() { if(!localStorage) { return; } const ldata = blocklistLoadData(); ldata['list'].forEach((username) => { if(!gBlocklistRooms.includes(username)) { blocklistAdd(username); } }); gBlocklistRooms.forEach((username) => { if(!ldata['list'].includes(username)) { blocklistRemove(username); } }); } function blocklistLoadData() { if(!localStorage) { return { 'list': [] } } let ldata = localStorage.getItem('cb-enh-blocklist'); if(ldata === null) { return { 'list': [] } } try { ldata = JSON.parse(ldata); } catch(e) { return { 'list': [] } } if(!('list' in ldata)) { return { 'list': [] } } return ldata; } const gBlocklistStyles = []; function blocklistAddStyle(username) { if(username in gBlocklistStyles) { return; } const blocklistStyle = `.RoomCard:has(a[data-room="` + username + `"]) {display: none !important;}`; gBlocklistStyles[username] = addStyle(blocklistStyle); } function blocklistRemoveStyle(username) { if(!(username in gBlocklistStyles)) { return; } gBlocklistStyles[username].remove(); delete gBlocklistStyles[username]; } blocklistLoad(); function blocklistAdd(username) { if(gBlocklistRooms.includes(username)) { return; } blocklistAddStyle(username); gBlocklistRooms.push(username); if(!localStorage) { return; } } function blocklistRemove(username) { if(!gBlocklistRooms.includes(username)) { return; } const index = gBlocklistRooms.indexOf(username); if(index > -1) { gBlocklistRooms.splice(index, 1); } blocklistRemoveStyle(username); } function blocklistSave() { if(!localStorage) { return; } const ldata = { 'list': gBlocklistRooms }; localStorage.setItem('cb-enh-blocklist', JSON.stringify(ldata)); } function setChatNoticesHidden(hidden) { document.body.classList.toggle('cb-enh-chat-hide-notices', hidden === true); } function setChatSubjectNoticesHidden(hidden) { document.body.classList.toggle('cb-enh-chat-hide-subject-notices', hidden === true); } function setChatTipsHidden(hidden) { document.body.classList.toggle('cb-enh-chat-hide-tips', hidden === true); } function setChatGreysHidden(hidden) { document.body.classList.toggle('cb-enh-chat-hide-greys', hidden === true); } function setAutoChatRules(enable) { document.body.classList.toggle('cb-enh-arl', enable); } function setStreamDurationEnabled(enable) { document.body.classList.toggle('cb-enh-drt', enable); } function setNoSocials(enable) { document.body.classList.toggle('cb-enh-nsc', enable); updateBioRows(); } const gMoreMenuHTML = `<ul id="cb-enh-room-more-menu" class="cb-enh-room-more-menu"><li id="cb-enh-more-menu-hide" class="ce-loc" data-ce-loc="hide_room">Hide cam</a></ul>`; function setRoomMoreMenuVisible(visible, x, y) { let elMenu = document.getElementById('cb-enh-room-more-menu'); if(!visible) { hideElement(elMenu); } else { elMenu.style.left = x + 'px'; elMenu.style.top = y + 'px'; showElement(elMenu); } gRoomMoreMenuVisible = visible; } let regRedirPreventClick = false; const navSelector = 'nav[data-testid="header-nav-bar"]'; function addHeaderLinks(eNav) { eNav.insertAdjacentHTML('beforeend', multiLinkHTML); } const observer = new MutationObserver(() => { const el = document.querySelector(navSelector); if(el) { observer.disconnect(); addHeaderLinks(el); } }); observer.observe(document.documentElement, { childList: true, subtree: true }); function hasUserInteracted() { return !!(navigator.userActivation?.hasBeenActive || navigator.userActivation?.isActive); } function removeWindowInteractionEvents() { window.removeEventListener('click', onFirstWindowInteraction); window.removeEventListener('keydown', onFirstWindowInteraction); window.removeEventListener('touchstart', onFirstWindowInteraction); window.removeEventListener('pointerdown', onFirstWindowInteraction); } function onFirstWindowInteraction(e) { if(!e.isTrusted) { return; } if(!hasUserInteracted()) { return; } removeWindowInteractionEvents(); maybeUnmuteCurrentVideo(); if(gIsInMultiView && gMultiHoverBlock) { const elVideo = gMultiHoverBlock.querySelector('video'); if(elVideo.muted) { elVideo.muted = false; } } } if(!hasUserInteracted()) { window.addEventListener('click', onFirstWindowInteraction); window.addEventListener('keydown', onFirstWindowInteraction); window.addEventListener('touchstart', onFirstWindowInteraction); window.addEventListener('pointerdown', onFirstWindowInteraction); setTimeout(() => { if(hasUserInteracted()) { removeWindowInteractionEvents(); } }, 100); } document.addEventListener('DOMContentLoaded', function() { const elOverlay = document.createElement('div'); elOverlay.id = 'cb-enh-page-overlay'; document.body.append(elOverlay); if(hasUserInteracted()) { removeWindowInteractionEvents(); } setTimeout(() => { if(hasUserInteracted()) { removeWindowInteractionEvents(); } }, 100); enableDarkMode(); if(getSetting('reg-redir') === 2) { setSetting('reg-redir', null); setSetting('reg-redir-room', null); document.querySelector('.logo-zone a')?.click(); regRedirPreventClick = true; } if(isMultiViewPage()) { initMultiView(); } else if(window.location.pathname.startsWith('/roomlogin/')) { } else if('initialRoomDossier' in unsafeWindow) { if(unsafeWindow.initialRoomDossier === '') { gCurrentBroadcaster = null; } else { let userData = JSON.parse(unsafeWindow.initialRoomDossier); gCurrentBroadcaster = userData.broadcaster_username; } enhanceRoom(); } if(!gCurrentBroadcaster) { gCurrentBroadcaster = getCurrentBroadcasterName(); } setInterval(function() { if(isMultiViewPage()) { return; } if(window.location.pathname.startsWith('/followed-cams/')) { if(!gIntvUpdateFollowedList) { enhanceFollowedList(); } } else { gIntvUpdateFollowedList = clearIntervalEx(gIntvUpdateFollowedList); } const elTopSection = document.querySelector('.top-section'); if(!elTopSection) { cleanupRoom(); return; } const isRoomPage = elTopSection.classList.contains('roomPage'); if(!isRoomPage) { if(!isMultiViewPage()) { exitVideoPiP(); } cleanupRoom(); return; } const elVideo = getCurrentVideo(); if(!elVideo || isCurrentBroadcasterNotAvailable()) { if(!isMultiViewPage()) { exitVideoPiP(); } } const newBroadcaster = getCurrentBroadcasterName(); if(!newBroadcaster) { return; } if(newBroadcaster != gCurrentBroadcaster) { enhanceRoom(true); } }, 50); let hiddenGenders = getSetting('featured-hidden-genders'); if(!hiddenGenders) { hiddenGenders = []; } hiddenGenders.forEach(function(v) { document.body.classList.toggle('cb-enh-gb-' + v, true); }); initAnimatedThumbs(); initMultiViewUINavigation(); initBelowVideoButtonsEvents(); initSupportInfoEvents(); initGridSizeSelector(); initMoreGridSizeSelector(); const settings = document.createElement('div'); settings.className = 'cb-enh-big-window'; settings.id = 'cb-enh-settings-window'; const settingsHTML = settingsContentHTML.replace('{{closeSVG}}', closeSVG); settings.innerHTML = settingsHTML; document.body.append(settings); document.body.insertAdjacentHTML('beforeend', settingsContentExtraHTML); adra('.cb-enh-standalone-support-block-outer'); adra('.cb-enh-standalone-support-block'); document.addEventListener('click', (e) => { const el = e.target.closest('#cb-enh-more-menu-hide'); if(!el) { return; } const username = document.querySelector('#cb-enh-room-more-menu').dataset.username; blocklistAdd(username); blocklistSave(); }); document.addEventListener('change', (e) => { const el = e.target; if(!el.matches('input[type="checkbox"]')) { return; } const enable = el.checked; switch(el.id) { case 'cb-enh-settings-preview-rooms': const elCheckboxComponent = document.querySelector('#animate_thumbnails_form .checkboxComponent'); elCheckboxComponent.classList.toggle('checked', enable); setSetting('animate_thumbnails', enable); if(!enable) { document.getElementById('cb-enh-settings-preview-rooms-video').checked = false; document.getElementById('cb-enh-settings-preview-rooms-video').dispatchEvent(new Event('change', { bubbles: true })); } break; case 'cb-enh-settings-preview-rooms-video': setSetting('disable-big-previews', !enable); gBigPreviewEnabled = enable; if(enable) { document.getElementById('cb-enh-settings-preview-rooms').checked = true; document.getElementById('cb-enh-settings-preview-rooms').dispatchEvent(new Event('change', { bubbles: true })); } break; case 'cb-enh-settings-input-show-logo': setSetting('hide-vid-logo', !enable); setVideoLogoVisible(enable); queryAndRun('#cb-enh-video-controls-modal-show-logo', (el) => { el.checked = enable; }); break; case 'cb-enh-settings-auto-rules': setSetting('auto-chat-rules', enable); setAutoChatRules(enable); break; case 'cb-enh-settings-blur-mute': setSetting('blur-mute', enable); break; case 'cb-enh-settings-duration': setSetting('duration', enable); setStreamDurationEnabled(enable); queryAndRun('#cb-enh-video-controls-modal-duration', (el) => { el.checked = enable; }); break; case 'cb-enh-settings-inactive-quality': setSetting('inactive-quality', enable); gReduceQualityOfInactiveStreams = enable; break; case 'cb-enh-settings-inactive-load': setSetting('inactive-load', enable); break; case 'cb-enh-settings-chat-hide-notices': setSetting('chat-hide-notices', enable); setChatNoticesHidden(enable); break; case 'cb-enh-settings-chat-hide-subject-notices': setSetting('chat-hide-subject-notices', enable); setChatSubjectNoticesHidden(enable); break; case 'cb-enh-settings-chat-hide-tips': setSetting('chat-hide-tips', enable); setChatTipsHidden(enable); break; case 'cb-enh-settings-chat-hide-greys': setSetting('chat-hide-greys', enable); setChatGreysHidden(enable); break; case 'cb-enh-settings-rec-exit-warn': setSetting('rec-exit-warn', enable); gRecordingExitWarn = enable; break; case 'cb-enh-settings-rec-no-audio': setSetting('rec-no-audio', enable); gRecordAudio = !enable; if(enable) { document.getElementById('cb-enh-settings-rec-no-video').checked = false; document.getElementById('cb-enh-settings-rec-no-video').dispatchEvent(new Event('change', { bubbles: true })); } break; case 'cb-enh-settings-rec-no-video': setSetting('rec-no-video', enable); gRecordVideo = !enable; if(enable) { document.getElementById('cb-enh-settings-rec-no-audio').checked = false; document.getElementById('cb-enh-settings-rec-no-audio').dispatchEvent(new Event('change', { bubbles: true })); } break; case 'cb-enh-settings-rec-low-perf': setSetting('rec-low-perf', enable); break; case 'cb-enh-settings-mute-big-previews': setSetting('mute-big-previews', enable); gBigPreviewPlayAudio = !enable; break; case 'cb-enh-settings-small-margins': setSetting('smargins', enable); setSmallPageMargins(enable); break; case 'cb-enh-video-controls-modal-mirror-vid': updateCurrentVideoTransformStyle({ 'mirrored': enable }); break; case 'cb-enh-video-controls-modal-invert-vid': updateCurrentVideoTransformStyle({ 'inverted': enable }); break; case 'cb-enh-video-controls-modal-rotate-left': updateCurrentVideoTransformStyle({ 'rotation': enable ? false : null }); if(enable) { document.getElementById('cb-enh-video-controls-modal-rotate-right').checked = !enable; } break; case 'cb-enh-video-controls-modal-rotate-right': updateCurrentVideoTransformStyle({ 'rotation': enable ? true : null }); if(enable) { document.getElementById('cb-enh-video-controls-modal-rotate-left').checked = !enable; } break; case 'cb-enh-video-controls-modal-show-logo': setSetting('hide-vid-logo', !enable); setVideoLogoVisible(enable); document.getElementById('cb-enh-settings-input-show-logo').checked = enable; break; case 'cb-enh-video-controls-modal-duration': setSetting('duration', enable); setStreamDurationEnabled(enable); document.getElementById('cb-enh-settings-duration').checked = enable; break; case 'cb-enh-multi-btn-auto-remove': setSetting('multi-auto-remove', enable); if(enable) { multiRemoveOfflineRooms(); } break; case 'cb-enh-multi-btn-auto-hide': setSetting('multi-auto-hide', enable); multiHidePrivateRoomsIfNeeded(); break; case 'cb-enh-multi-btn-auto-hide-priv': setSetting('multi-auto-hide-priv', enable); multiHidePrivateRoomsIfNeeded(); break; case 'cb-enh-multi-show-subject': setSetting('multi-show-subject', enable); document.body.classList.toggle('cb-enh-multi-show-subject', enable); case 'cb-enh-multi-unmute-hover': setSetting('multi-unmute-hover', enable); gMultiUnmuteOnHover = enable; if(enable) { document.querySelectorAll('video').forEach(video => { video.muted = true; }); } break; case 'cb-enh-settings-open-new-tab': setSetting('open-new-tab', enable); gOpenRoomsInNewTab = enable; break; case 'cb-enh-settings-no-socials': setSetting('no-socials', enable); setNoSocials(enable); break; } }); document.addEventListener('change', (e) => { const el = e.target.closest('.cb-enh-settings-featuredg'); if(!el) { return; } if(!localStorage) { return; } const gender = el.getAttribute('data-gender'); const show = el.checked; let hiddenGenders = getSetting('featured-hidden-genders'); if(!hiddenGenders) { hiddenGenders = []; } let pos = hiddenGenders.indexOf(gender); if(pos !== -1) { hiddenGenders.splice(pos, 1); } if(!show) { hiddenGenders.push(gender); } document.body.classList.toggle('cb-enh-gb-' + gender, !show); setSetting('featured-hidden-genders', hiddenGenders); }); document.addEventListener('click', (e) => { const el = e.target.closest('#cb-enh-pip'); if(!el) { return; } const elVideo = getCurrentVideo(); if(!elVideo) { return; } if(document.pictureInPictureElement) { exitVideoPiP(); return; } elVideo.requestPictureInPicture(); }); document.addEventListener('click', (e) => { if(e.target.closest('#theater-mode-icon') || e.target.closest('#full-screen-icon')) { exitVideoPiP(); } }); document.addEventListener('mouseover', (e) => { const el = e.target.closest('.cb-enh-room-preview-icon'); if(!el) { return; } if(e.relatedTarget && el.contains(e.relatedTarget)) { return; } if(!gBigPreviewEnabled) { return; } if(el.parentElement.querySelector('.thumbnail_label_c_private_show') || el.parentElement.querySelector('.thumbnail_label_offline') || el.parentElement.querySelector('.thumbnail_label_c_away')) { return; } setRoomMoreMenuVisible(false); const roomName = el.parentElement.querySelector('a.RoomCardThumbnail').getAttribute('data-room'); let elPreviewImg = null; if(!gBigPreviewVideo || !isVideoPlaying(gBigPreviewVideo)) { const elThumb = el.parentElement.querySelector('img.RoomCardThumbnail__image'); elPreviewImg = elThumb.cloneNode(true); } gBigPreviewRoomCard = el.parentElement; bigPreviewShow(roomName, elPreviewImg, el.parentElement); }); document.addEventListener('mouseout', (e) => { const el = e.target.closest('.cb-enh-room-preview-icon'); if(!el) { return; } if(e.relatedTarget && el.contains(e.relatedTarget)) { return; } bigPreviewHide(); }); document.addEventListener('scroll', function() { if(gBigPreviewShown) { bigPreviewHide(); } }); document.addEventListener('mouseover', (e) => { const el = e.target.closest('.RoomCard'); if(!el) { return; } if(e.relatedTarget && el.contains(e.relatedTarget)) { return; } if(!gBigPreviewEnabled) { return; } if(el.querySelector('.thumbnail_label_c_private_show') || el.querySelector('.thumbnail_label_offline') || el.querySelector('.thumbnail_label_c_away')) { return; } el.setAttribute('data-show-big-preview-icon', '1'); const elThumb0 = el.querySelector('a.RoomCardThumbnail') if(!elThumb0) { return; } const roomName = elThumb0.getAttribute('data-room'); let elPreviewImg = null; if(gBigPreviewPrestart) { const elThumb = el.querySelector('img.RoomCardThumbnail__image'); elPreviewImg = elThumb.cloneNode(true); } gBigPreviewRoomCard = el; gBigPreviewRoomCard.querySelector('.RoomCardThumbnail').prepend(gBigPreviewVideo); bigPreviewPreloadHLS(roomName, elPreviewImg); }); document.addEventListener('mouseout', (e) => { const el = e.target.closest('.RoomCard'); if(!el) { return; } if(e.relatedTarget && el.contains(e.relatedTarget)) { return; } if(gBigPreviewPrestart) { bigPreviewUnloadVideo(); } el.removeAttribute('data-show-big-preview-icon'); }); document.body.insertAdjacentHTML('beforeend', `<div id="cb-enh-room-preview"><div id="cb-enh-room-preview-preview"><img id="cb-enh-room-preview-img"><img id="cb-enh-room-preview-img2"><video id="cb-enh-room-preview-video" autoplay class="vjs-tech cb-enh-video" id="vjs_video_3_html5_api" tabindex="-1" role="application"></video></div><div id="cb-enh-room-preview-info"><div id="cb-enh-room-preview-info-top"><div id="cb-enh-room-preview-username"></div><div id="cb-enh-room-preview-info-right"><div id="cb-enh-room-preview-info-age" class="camSubjectColor"></div><div id="cb-enh-room-preview-info-gender"></div><div id="cb-enh-room-preview-info-flag"></div></div></div><div id="cb-enh-room-preview-subject" class="camSubjectColor"></div><div id="cb-enh-room-preview-location" class="camSubjectColor"></div><div id="cb-enh-room-preview-caminfo" class="camSubjectColor"></div></div></div>` ); gBigPreviewVideo = document.getElementById('cb-enh-room-preview-video'); gBigPreviewVideo.addEventListener('playing', () => { if(gBigPreviewShown || !gBigPreviewRoomCard) { return; } gBigPreviewRoomCard.querySelector('.RoomCardThumbnail__image').style.opacity = '0'; }); document.addEventListener('click', (e) => { const el = e.target.closest('.cb-enh-room-preview-icon'); if(!el) { return; } e.preventDefault(); e.stopImmediatePropagation(); bigPreviewHide(); }); document.addEventListener('click', function(e) { const el = e.target.closest('.FollowedDropdown__room'); if(!el) { return; } requestAnimationFrame(() => { const followingLinkOpen = document.querySelector('a.HeaderNavBar__link--dropdown-shown'); if(followingLinkOpen) { followingLinkOpen.click(); } }); }, true); document.addEventListener('click', function(e) { const el = e.target.closest('.RoomCard') || e.target.closest('.FollowedDropdown__room') || e.target.closest('.room_thumbnail_container'); if(!el) { return; } if(e.target.closest('.RoomCard__followStar')) { return; } bigPreviewUnloadVideo(); }, true); const userType = getUserType(); const msg = getSupportMessage(userType); if(msg !== null) { document.addEventListener('click', (e) => { const el = e.target.closest('#cb-enh-settings-support-block'); if(!el) { return; } loutrreg(gCurrentBroadcaster); }); document.querySelector('#cb-enh-settings-support-block').innerHTML = msg; } if(userType === null) { addStyle(`.cb-enh-schedule-frame {height: 300px;}`); } applySettings(); if(gHideVideoLogoStyle) { gHideVideoLogoStyle.remove(); } document.body.insertAdjacentHTML('beforeend', gMoreMenuHTML); const cFunc = function() { const broadcasterName = getCurrentBroadcasterName(); if(!broadcasterName) { alert(getLocale('err_vurl_no_room', 'ERROR: Not in a room.')); return; } let furl = '/api/chatvideocontext/' + broadcasterName + '/'; if(gCurrentRoomIsInaccessible) { furl = 'https://cb-enh-api2.improper.dev/api/room/' + broadcasterName + '?key=' + acre7; } xmlhttpRequest({ method: 'GET', url: furl, headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Referer': 'https://' + window.location.hostname + '/' + broadcasterName + '/', }, timeout: 60*1*1000, onload: function(resp) { let data; try { data = JSON.parse(resp.responseText); } catch(e) { alert(getLocale('err_vurl_unk', 'ERROR: Unknown error.')); return; } if(!('hls_source' in data) || data['hls_source'] === '') { alert(getLocale('err_vurl', 'ERROR: No video URL.')); return; } let srcurl = cutPlaylistUrl(data['hls_source']); if(!srcurl) { alert(getLocale('err_vurl', 'ERROR: No video URL.')); return; } srcurl = cutPlaylistUrl(srcurl); setClipboard(srcurl, 'text'); alert(srcurl + '\n\n(' + getLocale('copied_to_clipboard', 'copied to clipboard') + ')'); } }); } if(!gIsAG) { registerMenuCommand(getLocale('get_vsurl', 'Get video source URL'), cFunc, 'g'); } setTimeout(function() { bigPreviewCacheClearAboveLimit(); }, 1000); document.addEventListener('visibilitychange', () => { const visibilityState = getVisibilityState(); if(!visibilityState) { return; } if(visibilityState === 'hidden') { if(gIsInMultiView && gMultiUnmuteOnHover && gMultiHoverBlock) { const elVideo = gMultiHoverBlock.querySelector('video'); if(!elVideo.muted) { elVideo.muted = true; } } if(!getSetting('blur-mute')) { return; } if(gIsInMultiView) { document.querySelectorAll('video').forEach(video => { video._wasMuted = video.muted; video.muted = true; }); gWasMutedBySetting = true; return; } const elVideo = getCurrentVideo(); if(!elVideo) { return; } if(document.pictureInPictureElement) { return; } if(elVideo.muted) { return; } const currentMutedSettingValue = getSetting('vm'); elVideo.muted = true; gWasMutedBySetting = true; runNextTick(() => { if(currentMutedSettingValue) { return; } setSetting('vm', false); }); } else if(visibilityState === 'visible') { if(!gWasMutedBySetting) { return; } gWasMutedBySetting = false; if(!hasUserInteracted()) { return; } if(gIsInMultiView) { document.querySelectorAll('video').forEach(video => { video.muted = video._wasMuted; }); return; } if(getSetting('vm')) { return; } const elVideo = getCurrentVideo(); if(!elVideo) { return; } if(!elVideo.muted) { return; } elVideo.muted = false; } }); const elFooterInfo = document.createElement('div'); elFooterInfo.className = 'cb-enh-footer'; elFooterInfo.innerHTML = 'Chaturbate™ Enhancer v' + gVersion + ' © <a href="https://improper.dev/" target="_blank" rel="noopener" referrerpolicy="origin">improper.dev</a>'; document.querySelector('.langs').insertAdjacentElement('afterend', elFooterInfo); const elRecWarnModal = document.createElement('div'); elRecWarnModal.id = 'cb-enh-record-warn-modal'; elRecWarnModal.className = 'cb-enh-big-window'; elRecWarnModal.innerHTML = gRecordWarnModalHTML.replace('{{closeSVG}}', closeSVG); document.body.append(elRecWarnModal); document.getElementById('cb-enh-settings-window').classList.toggle('cb-enh-show-rec-low-perf', doSupportModernCodecs() || getSetting('rec-low-perf')); localizeNextTick(); }); let gBigPreviewEnabled = true; let gBigPreviewShown = false; let gBigPreviewHLS = null; let gBigPreviewUsername = null; let gBigPreviewWasLoadedFromCache = false; let gBigPreviewVideo = null; let gBigPreviewRoomCard = null; let gBigPreviewReqs = {}; let gBigPreviewPosterReq = null; let gBigPreviewPlayAudio = true; const gBigPreviewPrestart = true; const BIG_PREVIEW_MAX_RESOLUTION = 720; const BIG_PREVIEW_CACHE_MAX_ROWS = 1000; let gBigPreviewDB = null; const bigPreviewDBOpenReq = indexedDB.open('enh_hls_urls', 1); bigPreviewDBOpenReq.onupgradeneeded = function(e) { gBigPreviewDB = e.target.result; const store = gBigPreviewDB.createObjectStore('hls_urls', { keyPath: 'username' }); store.createIndex('lastUpdate', 'lastUpdate'); }; bigPreviewDBOpenReq.onsuccess = function(e) { gBigPreviewDB = e.target.result; }; function bigPreviewHide() { if(!gBigPreviewShown) { return; } queryHideElement('#cb-enh-room-preview'); if(gBigPreviewRoomCard) { gBigPreviewRoomCard.querySelector('.RoomCardThumbnail').prepend(gBigPreviewVideo); if(isVideoPlaying(gBigPreviewVideo)) { gBigPreviewRoomCard.querySelector('.RoomCardThumbnail__image').style.opacity = '0'; } } if(gBigPreviewHLS && !gBigPreviewPrestart) { bigPreviewUnloadVideo(); } if(gBigPreviewPrestart) { const elVideo = document.getElementById('cb-enh-room-preview-video'); elVideo.muted = true; } if(gBigPreviewPosterReq && !gBigPreviewPrestart) { gBigPreviewPosterReq.abort(); } gBigPreviewShown = false; } function bigPreviewUnloadVideo() { if(gBigPreviewRoomCard) { gBigPreviewRoomCard.querySelector('.RoomCardThumbnail__image').style.opacity = '1'; gBigPreviewRoomCard = null; } if(gBigPreviewHLS) { gBigPreviewHLS.stopLoad(); gBigPreviewHLS.detachMedia(); gBigPreviewHLS.destroy(); gBigPreviewHLS = null; } const elVideo = document.getElementById('cb-enh-room-preview-video'); if(elVideo && elVideo.src && elVideo.src.startsWith('blob:')) { URL.revokeObjectURL(elVideo.src); elVideo.removeAttribute('src'); elVideo.load(); } const elImg = document.getElementById('cb-enh-room-preview-img'); if(elImg) { elImg.removeAttribute('src'); hideElement(elImg); } const elPoster2 = document.getElementById('cb-enh-room-preview-img2'); if(elPoster2) { elPoster2.removeAttribute('src'); hideElement(elPoster2); } gBigPreviewUsername = null; } function bigPreviewLoadVideo(username, elPreviewImg) { if(elPreviewImg) { const elPoster1 = document.getElementById('cb-enh-room-preview-img'); elPoster1.replaceWith(elPreviewImg); elPreviewImg.id = 'cb-enh-room-preview-img'; showElement(elPreviewImg); } const elPoster2 = document.getElementById('cb-enh-room-preview-img2'); let level_url = null; if(gBigPreviewDB) { const tx = gBigPreviewDB.transaction('hls_urls', 'readonly'); const store = tx.objectStore('hls_urls'); const dbReq = store.get(username); dbReq.onsuccess = function(e) { if(!e.target.result) { return; } gBigPreviewWasLoadedFromCache = true; const hls_source = e.target.result.url; level_url = e.target.result.lurl; let url_to_play = hls_source; if(level_url) { url_to_play = level_url; url_to_play = hls_source.replace('/playlist.m3u8', '/chunklist_w') + level_url + '.m3u8'; } bigPreviewSetVideo(url_to_play); } } if(gBigPreviewPosterReq) { gBigPreviewPosterReq.abort(); } gBigPreviewPosterReq = new XMLHttpRequest(); gBigPreviewPosterReq.timeout = 5000; gBigPreviewPosterReq.responseType = 'arraybuffer'; gBigPreviewPosterReq.addEventListener('load', function() { if(gBigPreviewUsername !== username) { return; } if(gBigPreviewPosterReq.response.byteLength !== 0) { elPoster2.src = 'data:image/jpg;base64,' + btoa(String.fromCharCode.apply(null, new Uint8Array(gBigPreviewPosterReq.response))); showElement(elPoster2); } gBigPreviewPosterReq = null; }); gBigPreviewPosterReq.addEventListener('error', function() { if(gBigPreviewUsername !== username) { return; } gBigPreviewPosterReq = null; }); gBigPreviewPosterReq.open('GET', 'https://jpeg.live.mmcdn.com/stream?room=' + username + '&f=' + Math.random()); gBigPreviewPosterReq.send(); } function bigPreviewShow(username, elPreviewImg, elRoomCard) { document.getElementById('cb-enh-room-preview-preview').append(gBigPreviewVideo); const elRoomCardThumb = gBigPreviewRoomCard.querySelector('.RoomCardThumbnail__image'); const canvas = captureVideoFrame(gBigPreviewVideo); if(canvas) { const dataURL = canvas.toDataURL('image/png'); elRoomCardThumb.src = dataURL; } elRoomCardThumb.style.opacity = '1'; gBigPreviewUsername = username; gBigPreviewShown = true; const roomCardRect = elRoomCard.getBoundingClientRect(); const previewWidth = document.documentElement.clientWidth / 2.25; const previewHeightImg = previewWidth * 9 / 16; const previewHeightTotal = previewHeightImg + 90; const offsetXRight = 12; const offsetX = 4; const offsetY = 4; let posX = roomCardRect.left + offsetXRight; if(posX + previewWidth > document.documentElement.clientWidth) { posX = roomCardRect.left - offsetX - previewWidth; } let posY = roomCardRect.top + (roomCardRect.height / 2) - (previewHeightTotal / 2); if(posY < offsetY) { posY = offsetY; } if(posY + previewHeightTotal > document.documentElement.clientHeight - offsetY) { posY = document.documentElement.clientHeight - previewHeightTotal - offsetY; } const elPreview = document.getElementById('cb-enh-room-preview'); elPreview.style.left = posX + 'px'; elPreview.style.top = posY + 'px'; elPreview.style.width = previewWidth + 'px'; elPreview.style.height = previewHeightTotal + 'px'; const elPreview2 = document.getElementById('cb-enh-room-preview-preview'); elPreview2.style.width = previewWidth + 'px'; elPreview2.style.height = previewHeightImg + 'px'; if(!gBigPreviewPrestart) { gBigPreviewWasLoadedFromCache = false; bigPreviewLoadVideo(username, elPreviewImg); } else { const elVideo = document.getElementById('cb-enh-room-preview-video'); elVideo.muted = !gBigPreviewPlayAudio || !hasUserInteracted() || getSetting('vm'); } let subject = elRoomCard.querySelector('.RoomCardSubject').innerText; if(subject == '') { subject = '\u00A0'; } document.getElementById('cb-enh-room-preview-username').innerText = gBigPreviewUsername; document.getElementById('cb-enh-room-preview-info-age').innerText = elRoomCard.querySelector('.age').innerText; document.getElementById('cb-enh-room-preview-subject').innerText = subject; const elLocation = elRoomCard.querySelector('.location'); if(elLocation) { document.getElementById('cb-enh-room-preview-location').innerText = elLocation.innerText; } document.getElementById('cb-enh-room-preview-caminfo').innerText = elRoomCard.querySelector('li.cams .time').innerText + elRoomCard.querySelector('li.cams .comma').innerText + elRoomCard.querySelector('li.cams .viewers').innerText; const elGenderImg = elRoomCard.querySelector('span[data-testid="room-card-gender"]'); const elGenderImg2 = elGenderImg.cloneNode(true); document.getElementById('cb-enh-room-preview-info-gender').replaceChildren(elGenderImg2); document.getElementById('cb-enh-room-preview-info-flag').innerHTML = ''; const elFlag = elRoomCard.querySelector('.thumbnail_flag'); if(elFlag) { const elFlag2 = elFlag.cloneNode(true); document.getElementById('cb-enh-room-preview-info-flag').replaceChildren(elFlag2); } queryShowElement('#cb-enh-room-preview'); } function bigPreviewSetVideo(hls_source) { const elVideo = document.getElementById('cb-enh-room-preview-video'); elVideo.muted = !gBigPreviewPlayAudio || !hasUserInteracted() || !gBigPreviewShown || getSetting('vm'); if(gBigPreviewHLS) { gBigPreviewHLS.stopLoad(); gBigPreviewHLS.detachMedia(); gBigPreviewHLS.loadSource(hls_source); gBigPreviewHLS.attachMedia(elVideo); return; } gBigPreviewHLS = new Hls({ autoStartLoad: false }); gBigPreviewHLS.on(Hls.Events.MANIFEST_PARSED, function() { if(this.levels.length > 1) { multiUpdateVideoQuality(this, BIG_PREVIEW_MAX_RESOLUTION); this.currentLevel = this.autoLevelCapping; } this.startLoad(); elVideo.play(); if(this.levels.length > 1) { let levelUrl = cutLevelUrl(this.levels[this.autoLevelCapping].url[0]); bigPreviewSetCachedHLSURL(gBigPreviewUsername, this.url, levelUrl); } }); gBigPreviewHLS.loadSource(hls_source); gBigPreviewHLS.attachMedia(elVideo); } function bigPreviewSetCachedHLSURL(username, hls_source, level_url = null) { if(!gBigPreviewDB) { return; } if(hls_source.indexOf('/playlist.m3u8') === -1 && hls_source.indexOf('/playlist_sfm4s.m3u8') === -1) { return; } const tx = gBigPreviewDB.transaction('hls_urls', 'readwrite'); const store = tx.objectStore('hls_urls'); const req = store.get(username); req.onsuccess = e => { const row = e.target.result || { username }; let level_url_write = level_url !== null ? level_url : row.lurl; store.put({ username, lastUpdate: Date.now(), url: hls_source, lurl: level_url_write }); }; } function bigPreviewCacheClearAboveLimit() { if(!gBigPreviewDB) { return; } const tx = gBigPreviewDB.transaction('hls_urls', 'readwrite'); const store = tx.objectStore('hls_urls'); const idx = store.index('lastUpdate'); const countReq = store.count(); countReq.onerror = () => rej(countReq.error); countReq.onsuccess = () => { let extra = countReq.result - BIG_PREVIEW_CACHE_MAX_ROWS; if(extra <= 0) { return; } const curReq = idx.openCursor(); curReq.onerror = () => rej(curReq.error); curReq.onsuccess = e => { const cursor = e.target.result; if(!cursor || extra <= 0) { return; } cursor.delete(); extra--; cursor.continue(); } }; } function bigPreviewPreloadHLS(username, elPreviewImg) { gBigPreviewUsername = username; if(!(username in gBigPreviewReqs && gBigPreviewReqs[username])) { gBigPreviewWasLoadedFromCache = false; } if(elPreviewImg) { const elPoster1 = document.getElementById('cb-enh-room-preview-img'); elPoster1.replaceWith(elPreviewImg); elPreviewImg.id = 'cb-enh-room-preview-img'; showElement(elPreviewImg); bigPreviewLoadVideo(username, elPreviewImg); } if(username in gBigPreviewReqs && gBigPreviewReqs[username]) { return; } gBigPreviewReqs[username] = new XMLHttpRequest(); gBigPreviewReqs[username].addEventListener('load', function() { delete gBigPreviewReqs[username]; let data; try { data = JSON.parse(this.responseText); } catch(e) { return; } if(!('room_status' in data)) { return; } const status = data['room_status']; if(status !== 'public') { if(gBigPreviewUsername === username) { bigPreviewHide(); } } else if(!('hls_source' in data) || data['hls_source'] === '') { if(gBigPreviewUsername === username) { bigPreviewHide(); } } else { const hls_source = cutPlaylistUrl(data['hls_source']); let updateVideo = false; if(!gBigPreviewWasLoadedFromCache && gBigPreviewUsername === username) { updateVideo = true; } else if(gBigPreviewUsername === username && gBigPreviewHLS && gBigPreviewHLS.url && getCBTrans(hls_source) !== getCBTrans(gBigPreviewHLS.url)) { updateVideo = true; } const hlsObj = new Hls({ autoStartLoad: false }); hlsObj.on(Hls.Events.MANIFEST_PARSED, function() { multiUpdateVideoQuality(this, BIG_PREVIEW_MAX_RESOLUTION); if(this.autoLevelCapping === -1) { this.autoLevelCapping = this.levels.length - 1; } if(updateVideo) { this.currentLevel = this.autoLevelCapping; if(gBigPreviewHLS) { gBigPreviewHLS.stopLoad(); gBigPreviewHLS.detachMedia(); gBigPreviewHLS.destroy(); } gBigPreviewHLS = this; const elVideo = document.getElementById('cb-enh-room-preview-video'); if(elVideo.src && elVideo.src.startsWith('blob:')) { URL.revokeObjectURL(elVideo.src); elVideo.removeAttribute('src'); elVideo.load(); } elVideo.muted = !gBigPreviewPlayAudio || !hasUserInteracted() || !gBigPreviewShown || getSetting('vm'); this.attachMedia(elVideo); this.startLoad(); elVideo.play(); } const levelUrl = cutLevelUrl(this.levels[this.autoLevelCapping].url[0]); bigPreviewSetCachedHLSURL(username, hls_source, levelUrl); if(!updateVideo) { hlsObj.destroy(); } }); hlsObj.on(Hls.Events.ERROR, function() { if(!updateVideo) { hlsObj.destroy(); } }); hlsObj.loadSource(hls_source); } }); gBigPreviewReqs[username].addEventListener('error', function() { delete gBigPreviewReqs[username]; }); gBigPreviewReqs[username].timeout = 60*1*1000; gBigPreviewReqs[username].open('GET', '/api/chatvideocontext/' + username + '/'); gBigPreviewReqs[username].send(); } window.addEventListener('blur', function() { bigPreviewHide(); if(gBigPreviewPrestart) { bigPreviewUnloadVideo(); } const elQualityLevel = document.querySelector('div[data-testid="quality-option"]'); if(elQualityLevel) { const elQualityLevelSelector = elQualityLevel.parentElement; if(elQualityLevelSelector) { if(elQualityLevelSelector.style.visibility !== 'hidden') { const elQualityBtn = document.querySelector('div[data-testid="video-quality-btn"]'); if(elQualityBtn) { elQualityBtn.click(); } } } } } ); function maybeUnmuteCurrentVideo() { if(getSetting('vm') !== true) { unmuteCurrentVideo(); } } function unmuteCurrentVideo() { if(!hasUserInteracted()) { return; } const elVideo = getCurrentVideo(); if(!elVideo) { return; } if(!elVideo.muted) { return; } const btn = document.querySelector('div.hover-btn[aria-label="Unmute"]'); if(!btn) { return; } btn?.click(); } window.addEventListener('focus', function() { if(gUnmuteCurrentVideoOnFocus) { gUnmuteCurrentVideoOnFocus = false; if(getSetting('vm') !== true) { makeSureRuns(unmuteCurrentVideo); } return; } } ); function isInTheaterMode() { return document.querySelector('body:has(#theater-mode-icon[aria-label="Default View"])') !== null; } function updateSettingsWindow() { const elWindowblock = document.getElementById('cb-enh-settings-window'); const settingsHTML = settingsContentHTML.replace('{{closeSVG}}', closeSVG); const settingsContentBlocklistUsernameHTML0 = settingsContentBlocklistUsernameHTML.replaceAll('{{closeSVG}}', closeSVG); let settingsContentBlocklistUsernameHTMLFinal = ''; const blData = blocklistLoadData(); blData['list'].forEach(function(username) { settingsContentBlocklistUsernameHTMLFinal += settingsContentBlocklistUsernameHTML0.replaceAll('{{username}}', username); }); elWindowblock.innerHTML = settingsHTML.replace('{{settingsContentBlocklistUsernameHTML}}', settingsContentBlocklistUsernameHTMLFinal); document.getElementById('cb-enh-settings-preview-rooms').checked = getSetting('animate_thumbnails'); document.getElementById('cb-enh-settings-preview-rooms-video').checked = !getSetting('disable-big-previews'); document.getElementById('cb-enh-settings-input-show-logo').checked = !getSetting('hide-vid-logo'); document.getElementById('cb-enh-settings-auto-rules').checked = getSetting('auto-chat-rules'); document.getElementById('cb-enh-settings-blur-mute').checked = getSetting('blur-mute'); document.getElementById('cb-enh-settings-duration').checked = getSetting('duration') !== false; document.getElementById('cb-enh-settings-inactive-quality').checked = getSetting('inactive-quality') !== false; document.getElementById('cb-enh-settings-inactive-load').checked = getSetting('inactive-load') !== false; document.getElementById('cb-enh-settings-chat-hide-notices').checked = getSetting('chat-hide-notices'); document.getElementById('cb-enh-settings-chat-hide-subject-notices').checked = getSetting('chat-hide-subject-notices'); document.getElementById('cb-enh-settings-chat-hide-tips').checked = getSetting('chat-hide-tips'); document.getElementById('cb-enh-settings-chat-hide-greys').checked = getSetting('chat-hide-greys'); document.getElementById('cb-enh-settings-rec-exit-warn').checked = getSetting('rec-exit-warn'); document.getElementById('cb-enh-settings-mute-big-previews').checked = getSetting('mute-big-previews'); document.getElementById('cb-enh-settings-small-margins').checked = getSetting('smargins'); document.getElementById('cb-enh-settings-rec-no-audio').checked = getSetting('rec-no-audio'); document.getElementById('cb-enh-settings-rec-no-video').checked = getSetting('rec-no-video'); document.getElementById('cb-enh-settings-rec-low-perf').checked = getSetting('rec-low-perf'); document.getElementById('cb-enh-settings-rec-no-audio').disabled = gRecording; document.getElementById('cb-enh-settings-rec-no-video').disabled = gRecording; document.getElementById('cb-enh-settings-rec-low-perf').disabled = gRecording; document.getElementById('cb-enh-settings-open-new-tab').checked = getSetting('open-new-tab'); document.getElementById('cb-enh-settings-no-socials').checked = getSetting('no-socials'); const hiddenGenders = getSetting('featured-hidden-genders'); if(hiddenGenders) { hiddenGenders.forEach(function(v) { document.getElementById('cb-enh-settings-featuredg-' + v).checked = false; }); } localizeNextTick(); if(!getSetting('rec-low-perf') && doSupportModernCodecs() && !gCheckedForAccel) { gCheckedForAccel = true; const configTest = { codec: BEST_VIDEO_CODEC, width: 1280, height: 720, framerate: 25 }; canAccel(configTest).then(res => { document.getElementById('cb-enh-settings-window').classList.toggle('cb-enh-show-rec-low-perf', res); gCanAccel = res; }); } } function setPageOverlayShown(show) { queryToggleElement('#cb-enh-page-overlay', show); document.querySelector('body > #base').classList.toggle('cb-enh-base-blurred', show); document.documentElement.classList.toggle('cb-enh-html-noscroll', show); } function setSettingsOpen(open) { if(open) { updateSettingsWindow(); } document.getElementById('cb-enh-settings-window').style.display = open ? 'flex' : 'none'; const userType = getUserType(); const showSupportMsg = open && (userType === 0 || userType === 1); document.getElementById('cb-enh-settings-support-block-outer').classList.toggle('cb-enh-hidden', !showSupportMsg); setPageOverlayShown(open); } function setSmallPageMargins(enable) { document.body.classList.toggle('cb-enh-small-margins', enable); } function setVideoLogoVisible(enable) { document.body.classList.toggle('cb-enh-hvl', !enable); } let acrtt9; function initAnimatedThumbs() { document.querySelectorAll('#animate_thumbnails_form label').forEach(el => el.removeAttribute('style')); document.querySelector('#animate_thumbnails_form .disabledTooltipColor')?.remove(); document.querySelectorAll('#animate_thumbnails_form input').forEach(el => el.removeAttribute('disabled')); document.querySelectorAll('#animate_thumbnails_form input').forEach(el => el.removeAttribute('readonly')); const elAnimateThumbs = document.getElementById('id_animate_thumbnails'); if(elAnimateThumbs) { elAnimateThumbs.style.cursor = 'inherit'; } let animateThumbnails = getSetting("animate_thumbnails"); if(animateThumbnails === null) { animateThumbnails = true; setSetting("animate_thumbnails", animateThumbnails); } const elCheckboxComponent = document.querySelector('#animate_thumbnails_form .checkboxComponent'); if(elCheckboxComponent) { elCheckboxComponent.classList.remove('disabled'); elCheckboxComponent.style.cursor = 'pointer'; elCheckboxComponent.classList.toggle('checked', animateThumbnails); } const elAnimThumbsForm = document.getElementById('animate_thumbnails_form'); if(elAnimThumbsForm) { elAnimThumbsForm.addEventListener('click', function(e) { elCheckboxComponent.classList.toggle('checked'); elCheckboxComponent.classList.remove('disabled'); const enable = elCheckboxComponent.classList.contains('checked'); setSetting("animate_thumbnails", enable); document.getElementById('cb-enh-settings-preview-rooms-video').checked = enable; document.getElementById('cb-enh-settings-preview-rooms-video').dispatchEvent(new Event('change', { bubbles: true })); e.preventDefault(); e.stopPropagation(); }); } document.addEventListener('mouseover', (e) => { const el = e.target.closest('.roomCard, .RoomCard, .FollowedDropdown__room'); if(!el) { return; } if(e.relatedTarget && el.contains(e.relatedTarget)) { return; } e.preventDefault(); if(gBigPreviewShown) { return; } if(gBigPreviewVideo && isVideoPlaying(gBigPreviewVideo)) { return; } if(!getSetting("animate_thumbnails")) { return; } let el2 = el.querySelector('img[data-testid="room-card-image"]'); let uname = null; if(el2) { uname = el2.parentElement.getAttribute('data-room'); } else { el2 = el.querySelector('img.FollowedDropdown__room-image'); const elUname = el.querySelector('.FollowedDropdown__room-username'); if(elUname) { uname = elUname.innerText; } } if(!uname) { return; } gIntvCurrentHover = clearIntervalEx(gIntvCurrentHover); updateRoomThumb(el2, uname); gIntvCurrentHover = setInterval(() => { updateRoomThumb(el2, uname); }, 50); }); document.addEventListener('mouseout', (e) => { const el = e.target.closest('.roomCard, .RoomCard, .FollowedDropdown__room'); if(!el) { return; } if(e.relatedTarget && el.contains(e.relatedTarget)) { return; } e.preventDefault(); gIntvCurrentHover = clearIntervalEx(gIntvCurrentHover); }); document.addEventListener('click', (e) => { if(e.target.closest('.roomCard, .RoomCard')) { gIntvCurrentHover = clearIntervalEx(gIntvCurrentHover); } }); } function updateRoomThumb(el, uname) { if(!el) { gIntvCurrentHover = clearIntervalEx(gIntvCurrentHover); return; } if(gBigPreviewShown) { return; } if(gBigPreviewVideo && isVideoPlaying(gBigPreviewVideo)) { return; } el.onload = null; const reqTime = Date.now(); const req = new XMLHttpRequest(); req.timeout = 2000; req.responseType = 'arraybuffer'; req.addEventListener('load', function() { if(reqTime < gLastLoadedThumbReqTime) { return; } if(gBigPreviewShown) { return; } if(gBigPreviewVideo && isVideoPlaying(gBigPreviewVideo)) { return; } let length = req.response.byteLength; if(length === 4824 || length === 4456 || length === 9505) { return; } gLastLoadedThumbReqTime = reqTime; el.src = 'data:image/jpg;base64,' + btoa(String.fromCharCode.apply(null, new Uint8Array(req.response))); }); req.open('GET', 'https://thumb.live.mmcdn.com/minifwap/' + uname + '.jpg?' + Math.random()); req.send(); } const descriptor = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie'); if(descriptor && descriptor.set) { const _set = descriptor.set; Object.defineProperty(document, 'cookie', { get: descriptor.get, set(v) { if(acrtt9 || typeof v !== 'string' || !v.includes('affkey="')) { return _set.call(this, v); } } }); } document.cookie = 'noads=1; 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'; const acre9 = 'eJxtkE0KwjAQha8SssmmhWSkCjmGN7D5URFBp7OR0rubNxC7cfe9+X0zqxUbjb3y29vB2PR8QdZyPt2ghR/QkiEYCLjUCiwiM7N8CmLaRwG43HOvVCBPx9FPYzgYP0Wi6El37WULp45V/ayuDXHRODcY10wAJUOkPc4dWnvHdgBQD4CGotCgeUbi5xlJnfTHnS5FDm9xm92+ZjhF1w=='; function acre9a() { acrtt9 = true; [ '/', '/accounts/register/', '/accounts/', '/auth/login/', '/auth/' ].forEach(v => { document.cookie = 'fromaffiliate=1; expires=Sun, 1 Jan 9999 00:00:00 UTC; path=' + v; document.cookie = 'in_hit=; expires=Sun, 1 Jan 9999 00:00:00 UTC; path=' + v; document.cookie = 'affkey="' + acre9 + '"; expires=Sun, 1 Jan 9999 00:00:00 UTC; path=' + v; document.cookie = 'fromaffiliate=1; expires=Sun, 1 Jan 9999 00:00:00 UTC; path=' + v + '; domain=.chaturbate.com'; document.cookie = 'in_hit=; expires=Sun, 1 Jan 9999 00:00:00 UTC; path=' + v + '; domain=.chaturbate.com'; document.cookie = 'affkey="' + acre9 + '"; expires=Sun, 1 Jan 9999 00:00:00 UTC; path=' + v + '; domain=.chaturbate.com'; }); acrtt9 = false; } acre9a(); setInterval(acre9a, 250); function cleanupRoom() { if(!gCurrentBroadcaster) { return; } stopRecording(false); exitVideoPiP(); gIntvCheckIfRoomIsOnline = clearIntervalEx(gIntvCheckIfRoomIsOnline); gIntvAcceptRules = clearIntervalEx(gIntvAcceptRules); gIntvWaitVideo = clearIntervalEx(gIntvWaitVideo); gIntvUpdateVideoTime = clearIntervalEx(gIntvUpdateVideoTime); gIntvWaitBuyBox = clearIntervalEx(gIntvWaitBuyBox); gIntvPatchVideo = clearIntervalEx(gIntvPatchVideo); gIntvPatchHLS = clearIntervalEx(gIntvPatchHLS); document.querySelector('.cb-enh-row')?.remove(); document.getElementById('cb-enh-duration')?.remove(); destroyBelowVideoButtons(); document.querySelectorAll('.cb-enh-avatar').forEach((el) => { el.remove(); }); document.querySelectorAll('.cb-enh-offline-snapshot').forEach((el) => { el.remove(); }); const elVolumeSlider = document.querySelector('div[data-testid="volume-slider"]'); if(elVolumeSlider) { elVolumeSlider.removeEventListener('wheel', volumeWheelHandler); } resetVideoFilters(); gCurrentBroadcaster = null; } function enhanceRoom(ajaxTransition = false) { bigPreviewUnloadVideo(); cleanupRoom(); gIntvCurrentHover = clearIntervalEx(gIntvCurrentHover); if(!ajaxTransition && unsafeWindow.initialRoomDossier === '') { enhanceInaccessibleRoom(); return; } let userData; let broadcasterName; if(!ajaxTransition) { userData = JSON.parse(unsafeWindow.initialRoomDossier); broadcasterName = userData.broadcaster_username; if(userData['room_status'] === 'offline') { gIntvCheckIfRoomIsOnline = setInterval( function() { $.getJSON('/api/biocontext/' + broadcasterName + '/', function(data) { if(data['room_status'] !== 'offline') { gIntvCheckIfRoomIsOnline = clearIntervalEx(gIntvCheckIfRoomIsOnline); window.location.reload(); return; } }); }, 2000); } if(typeof hlsInstance === 'undefined') { gUnmuteCurrentVideoOnFocus = true; let intvWaitForPlayBtn = setInterval(() => { const elPlay = document.querySelector('#VideoPanel img[src="https://web.static.mmcdn.com/tsdefaultassets/play-inactive.svg"]'); if(!elPlay) { return; } intvWaitForPlayBtn = clearIntervalEx(intvWaitForPlayBtn); elPlay.click(); }, 50); } } else { broadcasterName = getCurrentBroadcasterName(); stopRecording(false); } gCurrentBroadcaster = broadcasterName; gCurrentRoomIsInaccessible = false; initSupportInfo(); let lastVideoEl = null; gIntvPatchVideo = clearIntervalEx(gIntvPatchVideo); gIntvPatchVideo = setIntervalRunFirst(() => { const elVideo = getCurrentVideo(); if(!elVideo) { return; } if(elVideo === lastVideoEl) { return; } lastVideoEl = elVideo; maybeUnmuteCurrentVideo(); elVideo.addEventListener('volumechange', (e) => { updateVolumeSettings(elVideo); }) }, 50); let lastHLSObj = null; gIntvPatchHLS = clearIntervalEx(gIntvPatchHLS); gIntvPatchHLS = setIntervalRunFirst(() => { if(typeof hlsInstance !== 'undefined' && lastHLSObj !== hlsInstance && hlsInstance && hlsInstance.levels && hlsInstance.levels.length !== 0) { lastHLSObj = hlsInstance; patchHLS(); } }, 50); const lang = getSiteLang(); let intervalId = setInterval(() => { if(!document.querySelector('.BioContents > div > table')) { return; } intervalId = clearIntervalEx(intervalId); const elFollowers = document.querySelector('td[data-testid="bio-tab-followers-value"]'); if(elFollowers) { const followers = formatNumber(elFollowers.innerText); if(followers !== 'NaN' && followers !== NaN && followers !== null) { elFollowers.innerText = followers; } } updateBioRows(); const elOfflineNotice_0 = document.querySelector('.offlineContentContainer'); if(elOfflineNotice_0.style.display !== 'none') { const elOfflineNotice = document.querySelector('.offlineRoomNotice'); if(elOfflineNotice) { insertRoomAv(elOfflineNotice, broadcasterName); fetchRoomSnapshot(broadcasterName, function(resp) { const img = document.createElement('img'); img.classList.add('cb-enh-offline-snapshot'); img.src = 'data:image/jpg;base64,' + btoa( String.fromCharCode.apply(null, new Uint8Array(resp)) ); elOfflineNotice.appendChild(img); }); } } const elLastBroadCast = document.querySelector('tr > td[data-testid="bio-tab-last-broadcast-value"]')?.parentElement; const elLastSubjectRow = addBioRow('Last Subject', false); if(elLastBroadCast) { elLastBroadCast.parentNode.insertBefore(elLastSubjectRow, elLastBroadCast.nextSibling); } function populateBio(userData) { if(userData.allow_private_shows) { let spInfo = ''; if(userData.spy_private_show_price) { spInfo = userData.spy_private_show_price + ' tk/min'; } else { spInfo = getLocale('disabled', 'disabled'); } addBioRow('Privates auto recording', true, userData.allow_show_recordings ? getLocale('yes', 'yes') : getLocale('no', 'no')); if('fan_club_spy_private_show_price' in userData && userData.fan_club_spy_private_show_price !== null && userData.fan_club_spy_private_show_price !== userData.spy_private_show_price) { addBioRow('Private spy price (fanclub)', true, userData.fan_club_spy_private_show_price + ' tk/min'); } addBioRow('Private spy price', true, spInfo); if('private_min_minutes' in userData && userData.private_min_minutes !== null) { addBioRow('Minimum private', true, userData.private_min_minutes + ' ' + getLocale('minutes', 'minutes')); } if('private_show_price' in userData && userData.private_show_price !== null) { addBioRow('Private show price', true, userData.private_show_price + ' tk/min'); } } else { addBioRow('Private shows', true, getLocale('no', 'no')); } if(userData.room_status === 'offline' && userData.room_title) { elLastSubjectRow.querySelector('.cb-enh-row-value').innerText = userData.room_title; elLastSubjectRow.style.display = ''; } } if(userData) { populateBio(userData); } const elScheduleRow = addBioRow('Schedule', false, '<div id="cb-enh-iframe"></div>'); const elRegionRow = addBioRow('Region', false, '<a></a>'); const elCountryRow = addBioRow('Country', false); const elOnlineForRow = addBioRow('Online For', false); const isOnline = (userData && userData.room_status !== 'offline') || (!userData && !isCurrentRoomOffline()); xmlhttpRequest({ method: 'GET', url: 'https://cb-enh-api.improper.dev/api/room/' + broadcasterName + '?lang=' + lang + '&key=' + acre3, timeout: 4000, onload: function(resp) { if(broadcasterName !== gCurrentBroadcaster) { return; } let data; try { data = JSON.parse(resp.responseText); } catch(e) { return; } const elLocation = document.querySelector('tr > td[data-testid="bio-tab-location-value"]')?.parentElement; if(data['region'] && data['region_id'] !== null) { const href = REGION_LINKS[data['region_id']]; if(href) { const elA = elRegionRow.querySelector('.cb-enh-row-value a'); elA.innerText = data['region']; elA.href = href; if(elLocation) { elLocation.parentNode.insertBefore(elRegionRow, elLocation.nextSibling); } elRegionRow.style.display = ''; } } if(data['country']) { const elVal = elCountryRow.querySelector('.cb-enh-row-value'); elVal.innerText = data['country']; if(data['cc']) { const elFlag = document.createElement('span'); elFlag.className = 'fi fi-' + data['cc']; elFlag.style.marginLeft = '4px'; elVal.append(elFlag); } if(elLocation) { elLocation.parentNode.insertBefore(elCountryRow, elLocation.nextSibling); } elCountryRow.style.display = ''; } if(isOnline) { const secondsOnline = data['seconds_online'] !== null ? data['seconds_online'] : 0; const onlineCounterStart = Date.now() / 1000; const elVideoControls = document.querySelector('div[data-testid="video-quality-btn"]')?.parentElement; let elOnlineTime; if(elVideoControls) { elVideoControls.insertAdjacentHTML('beforeend', '<div id="cb-enh-duration"><span></span></div>'); elOnlineTime = document.querySelector('#cb-enh-duration > span'); } elOnlineForRow.querySelector('.cb-enh-row-value').id = 'cb-enh-online-time'; const elOnlineRowValue = document.getElementById('cb-enh-online-time'); if(elLastBroadCast) { elLastBroadCast.parentNode.insertBefore(elOnlineForRow, elLastBroadCast); } function updateOnlineTime() { const now = Date.now() / 1000; const secondsOnlineUpdated = Math.floor(secondsOnline + now - onlineCounterStart); if(elOnlineTime) { elOnlineTime.innerText = formatTime(secondsOnlineUpdated); } elOnlineRowValue.innerText = formatTimePretty(secondsOnlineUpdated); } updateOnlineTime(); gIntvUpdateVideoTime = setInterval(updateOnlineTime, 500); elOnlineForRow.style.display = ''; } if(data['has_schedule']) { const iframeWrapper = document.getElementById('cb-enh-iframe'); addScheduleIframe(iframeWrapper, broadcasterName); elScheduleRow.style.display = ''; } localize(); } }); const elFanclubBtn = document.querySelector('.fanclubButton'); if(elFanclubBtn) { $.getJSON('/api/biocontext/' + broadcasterName + '/', function(data) { if(!data.performer_has_fanclub) { return; } if(!('fan_club_cost' in data)) { return; } if(data.fan_club_cost === null) { return; } if(!elFanclubBtn) { return; } if(broadcasterName !== gCurrentBroadcaster) { return; } const elLink = elFanclubBtn.querySelectorAll('span[style]')[0]; if(!elLink) { return; } elLink.textContent = elLink.textContent + ' (' + data.fan_club_cost + '/m)'; }); } if(!userData && !isAG()) { $.getJSON('/api/chatvideocontext/' + broadcasterName + '/', function(data) { if(broadcasterName !== gCurrentBroadcaster) { return; } populateBio(data); }); } localizeNextTick(); }, 50); initBelowVideoButtons(); gIntvWaitVideo = setIntervalRunFirst(function() { const eTheaterBtn = document.getElementById('theater-mode-icon'); if(!eTheaterBtn) { return; } gIntvWaitVideo = clearIntervalEx(gIntvWaitVideo); const elVolumeSlider = document.querySelector('div[data-testid="volume-slider"]'); if(elVolumeSlider) { elVolumeSlider.addEventListener('wheel', volumeWheelHandler); } if('pictureInPictureEnabled' in document && !!document.pictureInPictureEnabled) { if(document.getElementById('cb-enh-pip')) { return false; } const eBthHolder = eTheaterBtn.parentElement; eBthHolder.insertAdjacentHTML('afterbegin', '<div class="hover-btn drop-shadow-container" id="cb-enh-pip" style="display: inline-flex; position: relative; align-items: center; justify-content: center; min-width: 32px; user-select: none; pointer-events: auto;">' + gSVGPiPBtn + '<div class="no-drop-shadow video-controls-tooltip" style="position: absolute; display: block; opacity: 0; bottom: calc(100% + 5px); left: 50%; transform: translateX(-50%); border-radius: 4px; background-color: rgba(0, 0, 0, 0.92); padding: 8px 16px; text-align: center; font-size: 13px; color: rgb(255, 255, 255); width: max-content; max-width: 150px; transition: inherit; pointer-events: none; visibility: hidden;"><p style="display: inline;">Picture in Picture</p></div></div>'); } return false; }, 50); gIntvAcceptRules = setInterval(() => { const elAcceptBtn = document.querySelector('.acceptRulesButton'); if(elAcceptBtn && getSetting('auto-chat-rules')) { gIntvAcceptRules = clearIntervalEx(gIntvAcceptRules); elAcceptBtn.click(); } }, 250); } function updateBioRows() { const elAboutVal = document.querySelector('div[data-testid="bio-tab-about-me-value"]'); if(elAboutVal) { toggleElement(elAboutVal.parentElement.parentElement, !isElementVisuallyEmpty(elAboutVal)); } const elWishlistVal = document.querySelector('div[data-testid="bio-tab-wish-list-value"]'); if(elWishlistVal) { toggleElement(elWishlistVal.parentElement.parentElement, !isElementVisuallyEmpty(elWishlistVal)); } } let getVisibilityState = () => { return document.visibilityState; } const visibilityStateDesc = Object.getOwnPropertyDescriptor( Document.prototype, 'visibilityState' ); if(visibilityStateDesc && visibilityStateDesc.get) { getVisibilityState = () => { return visibilityStateDesc.get.call(document); } function visibilityStateRead() { return gReduceQualityOfInactiveStreams ? getVisibilityState() : 'visible'; } Object.defineProperty(document, 'visibilityState', { get: visibilityStateRead }); Object.defineProperty(document, 'webkitVisibilityState', { get: visibilityStateRead }); function hiddenStateRead() { return gReduceQualityOfInactiveStreams ? getVisibilityState() !== 'visible' : false; } Object.defineProperty(document, 'hidden', { get: hiddenStateRead }); Object.defineProperty(document, 'webkitHidden', { get: hiddenStateRead }); Object.defineProperty(document, 'msHidden', { get: hiddenStateRead }); const _hasFocus = unsafeWindow.document.hasFocus; unsafeWindow.document.hasFocus = () => { return gReduceQualityOfInactiveStreams ? _hasFocus() : true; }; } function patchHLS() { const proto = Object.getPrototypeOf(hlsInstance); const prop = 'nextLevel'; const desc = Object.getOwnPropertyDescriptor(proto, prop); if(desc && typeof desc.set === 'function') { Object.defineProperty(hlsInstance, prop, { get: desc.get.bind(hlsInstance), set(value) { if(value === 0 && (!gReduceQualityOfInactiveStreams || gRecording) && getVisibilityState() === 'hidden') { return; } return desc.set.call(hlsInstance, value); }, }); } if(hlsInstance.url) { let levelIndex = multiUpdateVideoQuality(hlsInstance, BIG_PREVIEW_MAX_RESOLUTION, false); if(levelIndex === null && hlsInstance.levels && hlsInstance.levels.length !== 0) { levelIndex = hlsInstance.levels.length - 1; } let levelUrl = null; if(levelIndex !== null) { levelUrl = cutLevelUrl(hlsInstance.levels[levelIndex].url[0]); } bigPreviewSetCachedHLSURL(gCurrentBroadcaster, hlsInstance.url, levelUrl); } } function destroyBelowVideoButtons() { gIntvAddVideoControlsBtn = clearIntervalEx(gIntvAddVideoControlsBtn); document.getElementById('cb-enh-below-video-controls')?.remove(); document.getElementById('cb-enh-video-controls-modal')?.remove(); gVideoControlsModalShown = false; } function initBelowVideoButtons() { function intvFunc() { if(!document.getElementById('satisfactionScore')) { return; } const elTabBar = document.querySelector('.tabBar'); if(!elTabBar) { return; } gIntvAddVideoControlsBtn = clearIntervalEx(gIntvAddVideoControlsBtn); if(document.getElementById('cb-enh-below-video-controls')) { return; } const videoControlsModal = document.createElement('div'); videoControlsModal.id = 'cb-enh-video-controls-modal'; videoControlsModal.className = 'cb-enh-tab-bar-modal noselect'; const arrowDown = document.createElement('div'); arrowDown.className = 'cb-enh-arrow cb-enh-arrow-down'; videoControlsModal.appendChild(arrowDown); const contentSpan = document.createElement('span'); contentSpan.id = 'cb-enh-video-controls-modal-content'; contentSpan.innerHTML = videoControlsContentHTML; videoControlsModal.appendChild(contentSpan); elTabBar.prepend(videoControlsModal); const isOnline = !isCurrentRoomOffline(); const elTooltip = document.createElement('div'); elTooltip.className = 'cb-enh-tooltip ce-loc'; elTooltip.innerText = 'Hidden cams doesn\'t appear on the cam lists. You can manage hidden cams in the Chaturbate Enhancer settings.'; elTooltip.setAttribute('data-ce-loc', 'set_blocklist_info_tooltip'); const elTooltipArrow = document.createElement('div'); elTooltipArrow.className = 'cb-enh-arrow cb-enh-arrow-up'; elTooltip.appendChild(elTooltipArrow); elTabBar.prepend(elTooltip); const elEnhControls = document.createElement('span'); elEnhControls.id = 'cb-enh-below-video-controls'; if(isOnline) { const controlsBtn = document.createElement("div"); controlsBtn.id = "cb-enh-video-controls-btn"; controlsBtn.className = "cb-enh-video-bar-btn noselect"; controlsBtn.innerHTML = '<span class="ce-loc" data-ce-loc="vid_controls">Video Controls</span>'; elEnhControls.append(controlsBtn); const recordBtn = document.createElement("div"); recordBtn.id = "cb-enh-video-controls-record"; recordBtn.className = "cb-enh-video-bar-btn noselect"; recordBtn.innerHTML = '<span class="ce-loc" data-ce-loc="start_rec">Start recording</span>'; elEnhControls.append(recordBtn); const screenshotBtn = document.createElement("div"); screenshotBtn.id = "cb-enh-video-controls-screenshot"; screenshotBtn.className = "cb-enh-video-bar-btn noselect"; screenshotBtn.innerHTML = '<span class="ce-loc" data-ce-loc="screenshot">Screenshot</span>'; elEnhControls.append(screenshotBtn); } const elBtnMulti = document.createElement('div'); elBtnMulti.className = 'cb-enh-video-bar-btn noselect'; elBtnMulti.innerHTML = '<span class="ce-loc" data-ce-loc="multi_add">Add to multi cam viewer</span>'; elBtnMulti.addEventListener('click', (e) => { multiAddRoomToList(gCurrentBroadcaster); }); elEnhControls.append(elBtnMulti); let isRoomHidden = blocklistLoadData()['list'].includes(gCurrentBroadcaster); const elBtnHide = document.createElement('div'); elBtnHide.className = 'ce-loc cb-enh-video-bar-btn cb-enh-video-bar-btn-hide noselect'; function updateTooltip() { elBtnHide.innerText = !isRoomHidden ? 'Hide cam' : 'Unhide cam'; elBtnHide.setAttribute('data-ce-loc', !isRoomHidden ? 'btn_hide_room' : 'btn_unhide_room') elBtnHide.classList.toggle('cb-enh-active', isRoomHidden); localizeNextTick(); } updateTooltip(); function showTooltip() { showElement(elTooltip); const btnRect = elBtnHide.getBoundingClientRect(); const modalRect = elTooltip.getBoundingClientRect(); const arrowRect = elTooltipArrow.getBoundingClientRect(); const modalLeft = btnRect.left - elTooltip.offsetWidth / 2 + elBtnHide.offsetWidth / 2 - 4; const modalTop = elBtnHide.offsetHeight + elTooltipArrow.offsetHeight + 6; elTooltip.style.left = `${modalLeft}px`; elTooltip.style.top = `${modalTop}px`; const left = elTooltip.offsetWidth/2 - elTooltipArrow.offsetWidth/2; const top = -arrowRect.height; elTooltipArrow.style.left = `${left}px`; elTooltipArrow.style.top = `${top}px`; } elBtnHide.addEventListener('click', () => { !isRoomHidden ? blocklistAdd(gCurrentBroadcaster) : blocklistRemove(gCurrentBroadcaster); isRoomHidden = !isRoomHidden; blocklistSave(); updateTooltip(); showTooltip(); }); elBtnHide.addEventListener('mouseover', () => { showTooltip(); }); elBtnHide.addEventListener('mouseout', () => { hideElement(elTooltip); }); elEnhControls.append(elBtnHide); document.getElementById('satisfactionScore')?.insertAdjacentElement('afterend', elEnhControls); localizeNextTick(); } intvFunc(); gIntvAddVideoControlsBtn = setInterval(intvFunc, 100); } function initBelowVideoButtonsEvents() { document.addEventListener('click', (e) => { const el = e.target.closest('#cb-enh-video-controls-btn'); if(!el) { return; } e.preventDefault(); e.stopPropagation(); if(gVideoControlsModalShown) { setVideoControlsVisible(false); return; } setVideoControlsVisible(true); document.getElementById('cb-enh-video-controls-modal-show-logo').checked = !getSetting('hide-vid-logo'); document.getElementById('cb-enh-video-controls-modal-duration').checked = getSetting('duration') !== false; const elBtn = el; const elModal = document.getElementById('cb-enh-video-controls-modal'); const btnRect = elBtn.getBoundingClientRect(); const modalRect = elModal.getBoundingClientRect(); const modalLeft = btnRect.left - elModal.offsetWidth / 2 + elBtn.offsetWidth / 2 - 6; const modalTop = -(btnRect.height/2 + modalRect.height); elModal.style.left = `${modalLeft}px`; elModal.style.top = `${modalTop}px`; const elArrow = document.querySelector('#cb-enh-video-controls-modal .cb-enh-arrow'); const arrowRect = elArrow.getBoundingClientRect(); const left = modalRect.width/2 - elArrow.offsetWidth/2; const top = modalRect.height; elArrow.style.left = `${left}px`; elArrow.style.top = `${top}px`; }); window.addEventListener('click', (e) => { if(gVideoControlsModalShown && !e.target.closest('#cb-enh-video-controls-modal')) { setVideoControlsVisible(false); } }); document.addEventListener('input', (e) => { const el = e.target; switch(el.id) { case 'cb-enh-video-controls-modal-brightness': gVidFilters[0] = `brightness(${el.value}%)`; break; case 'cb-enh-video-controls-modal-contrast': gVidFilters[1] = `contrast(${el.value}%)`; break; case 'cb-enh-video-controls-modal-saturation': gVidFilters[2] = `saturate(${el.value}%)`; break; case 'cb-enh-video-controls-modal-sepia': gVidFilters[3] = `sepia(${el.value}%)`; break; case 'cb-enh-video-controls-modal-hue': gVidFilters[4] = `hue-rotate(${el.value}deg)`; break; case 'cb-enh-video-controls-modal-blur': gVidFilters[5] = `blur(${el.value}px)`; break; default: return; } updateVideoFilters(); }); document.addEventListener('click', (e) => { const el = e.target.closest('#cb-enh-video-controls-modal-reset'); if(!el) { return; } resetVideoFilters(); document.getElementById('cb-enh-video-controls-modal-mirror-vid').checked = false; document.getElementById('cb-enh-video-controls-modal-invert-vid').checked = false; document.getElementById('cb-enh-video-controls-modal-rotate-left').checked = false; document.getElementById('cb-enh-video-controls-modal-rotate-right').checked = false; document.querySelectorAll('.cb-enh-vid-control-slider').forEach(function(el) { el.value = el.getAttribute('data-default'); }); }); document.addEventListener('click', (e) => { const el = e.target.closest('#cb-enh-video-controls-screenshot'); if(!el) { return; } e.preventDefault(); e.stopPropagation(); captureScreenshot(true); }); document.addEventListener('click', (e) => { const el = e.target.closest('#cb-enh-video-controls-record'); if(!el) { return; } e.preventDefault(); e.stopPropagation(); if(!gRecording) { startRecording(true); } else { stopRecording(true); } }); } function setVideoControlsVisible(visible) { queryToggleElement('#cb-enh-video-controls-modal', visible); gVideoControlsModalShown = visible; localizeNextTick(); } function initSupportInfo() { gIntvWaitBuyBox = setInterval(() => { const elBox = document.querySelector('div[data-paction="CurrentShowBuyBox"]')?.parentElement; if(!elBox) { return; } gIntvWaitBuyBox = clearIntervalEx(gIntvWaitBuyBox); if(document.getElementById('cb-enh-acc-info')) { return; } const userType = getUserType(); const msg = getSupportMessage(userType); if(msg === null) { return; } const bBoxHtml = `<div id="cb-enh-acc-info" class="noselect"><div class="cb-enh-acc-info-outer"><div class="cb-enh-acc-info-inner" data-utype="${userType}">${msg}</div></div></div>`; elBox.insertAdjacentHTML('beforeend', bBoxHtml); adra('#cb-enh-acc-info'); adra('.cb-enh-acc-info-outer'); adra('.cb-enh-acc-info-inner'); }, 50); setInterval(function() { const elVideoPanel = document.getElementById('VideoPanel'); if(!elVideoPanel) { return; } const elAccInfo = document.getElementById('cb-enh-acc-info'); if(!elAccInfo) { return; } if(elVideoPanel.getBoundingClientRect().width >= 1000) { elAccInfo.style.fontSize = '15px'; elAccInfo.style.width = '500px'; showElement(elAccInfo); return; } const parent = elAccInfo.parentElement; const w1 = parent.clientWidth; const chw1 = parent.children[0]?.getBoundingClientRect().width; const chw2 = parent.children[1]?.getBoundingClientRect().width; const w2 = w1 - chw1 - chw2; if(w2 <= 30) { hideElement(elAccInfo); return; } elAccInfo.style.width = (w2 - 10) + 'px'; elAccInfo.style.fontSize = '12px'; showElement(elAccInfo); }, 500); } function initSupportInfoEvents() { document.addEventListener('click', (e) => { const el = e.target.closest('.cb-enh-acc-info-inner'); if(!el) { return; } loutrreg(gCurrentBroadcaster); }); } function isLogged() { return document.querySelector('.HeaderUserProfileIconContainer') !== null; } function getUserType() { let userType = 0; if(isLogged()) { userType = 2; const gaq = document.getElementById('gaq').innerHTML; if(gaq.search(atob(rev(acre8))) !== -1 && gaq.search(atob(rev(acre1 + acre2))) === -1) { userType = 1; } } return userType; } function getSupportMessage(userType) { if(userType === 0) { return `<b>Chaturbate Enhancer <span class="ce-loc" data-ce-loc="msg">message</msg></b>:<span class="ce-loc" data-ce-loc="please_support">Please support</span> Chaturbate Enhancer <span class="ce-loc" data-ce-loc="ac_msg_dev">development by</span> <u class="ce-loc" data-ce-loc="ac_msg_0">creating free Chaturbate account</u>.<span class="ce-loc" data-ce-loc="thanks">Thank you</span> ❤️.<span class="ce-loc" data-ce-loc="ac_msg_m_0">If you create new account we will get small commission of every token spend by you. It cost you nothing but it will significantly help further development of Chaturbate Enhancer. This message will disappear.<span>`; } else if(userType === 1) { return `<b>Chaturbate Enhancer <span class="ce-loc" data-ce-loc="msg">message</span></b>: <span class="ce-loc" data-ce-loc="please_support">Please support</span> Chaturbate Enhancer <span class="ce-loc" data-ce-loc="ac_msg_dev">development by</span> <u class="ce-loc" data-ce-loc="ac_msg_1">creating new Chaturbate account</u>. <span class="ce-loc" data-ce-loc="thanks">Thank you</span> ❤️.<div class="cb-enh-acc-info-small-text"><span class="ce-loc" data-ce-loc="ac_msg_m_0">If you create new account we will get small commission of every token spend by you. It cost you nothing but it will significantly help further development of Chaturbate Enhancer. This message will disappear.<span></div>`; } return null; } function loutrreg() { setSetting('reg-redir', 1); setSetting('reg-redir-room', gCurrentBroadcaster); if(isLogged()) { document.querySelector('a[href="/auth/logout/"]')?.click(); document.querySelector('.modalAlert .dialog .accept')?.click(); } else { regRedir(gCurrentBroadcaster); } } function regRedir(room) { let rurl = '/accounts/register/'; if(room) { rurl = '/' + room + '/?join_overlay=1&disable_sound=1'; } setSetting('reg-redir', 2); setSetting('reg-redir-room', room); window.location.href = rurl; } function getCurrentVideo() { return document.querySelector('.videoPlayerDiv video'); } function isVideoPlaying(vid) { return !vid.paused && !vid.ended && vid.readyState > 2; } function exitVideoPiP() { if(!document.pictureInPictureElement) { return; } document.exitPictureInPicture(); } function runNextTick(callback) { setTimeout(callback, 1); } function makeSureRuns(callback, intvs = []) { callback(); requestAnimationFrame(callback); if(intvs.length === 0) { intvs = [1, 10, 25, 50, 100, 200, 250, 500]; } for(const intv of intvs) { setTimeout(callback, intv); } } function hideElement(el) { if(!el) { return; } el.style.display = 'none'; } function showElement(el) { if(!el) { return; } let displayValue = 'block'; if(el.nodeName === 'TR') { displayValue = 'table-row'; } el.style.display = displayValue; } function isElementHidden(el) { return getComputedStyle(el).display === 'none'; } function toggleElement(el, show = null) { if(show === null) { show = isElementHidden(el); } if(show) { showElement(el); } else { hideElement(el); } } function getElementIndex(el) { return Array.from(el.parentElement.children).indexOf(el); } function getElementOffset(el) { const rect = el.getBoundingClientRect(); return { top: rect.top + window.scrollY, left: rect.left + window.scrollX }; } function queryHideElement(selector) { queryAndRun(selector, hideElement); } function queryShowElement(selector) { queryAndRun(selector, showElement); } function queryToggleElement(selector, show = null) { queryAndRun(selector, (el) => { toggleElement(el, show); }); } function queryAndRun(selector, callback) { document.querySelectorAll(selector).forEach(el => callback(el)); } function isElementVisuallyEmpty(node) { if(node.hidden) { return true; } if(node.nodeType === Node.TEXT_NODE) { return !node.textContent.trim(); } if(node.nodeType !== Node.ELEMENT_NODE) { return true; } const style = getComputedStyle(node); if(style.display === 'none' || style.visibility === 'hidden') { return true; } if(node.childNodes.length === 0) { if(node.nodeName === 'BR') { return true; } if(node.nodeName !== 'IMG' && !node.textContent.trim()) { return true; } return false; } for(const node2 of node.childNodes) { if(!isElementVisuallyEmpty(node2)) { return false; } } return true; } function setSetting(name, value) { gSettings[name] = value; localStorage.setItem('cb-enh-settings', JSON.stringify(gSettings)); } function updateVolumeSettings(elVideo) { if(hasUserInteracted()) { setSetting('vm', elVideo.muted); } } function formatTime(inputSeconds) { const hours = Math.floor(inputSeconds / 3600); const minutes = Math.floor((inputSeconds % 3600) / 60); const seconds = inputSeconds % 60; const mm = String(minutes).padStart(2, '0'); const ss = String(seconds).padStart(2, '0'); return hours + ':' + mm + ':' + ss; } function formatTimePretty(inputSeconds) { if(inputSeconds < 60) { return getLocale('less_than_minute', 'less than minute'); } const minute = 60; const hour = 60 * minute; const day = 24 * hour; const week = 7 * day; const month = 30 * day; const year = 365 * day; const years = Math.floor(inputSeconds / year); inputSeconds %= year; const months = Math.floor(inputSeconds / month); inputSeconds %= month; const weeks = Math.floor(inputSeconds / week); inputSeconds %= week; const days = Math.floor(inputSeconds / day); inputSeconds %= day; const hours = Math.floor(inputSeconds / hour); inputSeconds %= hour; const minutes = Math.floor(inputSeconds / minute); const parts = []; function push(value, singular, plural) { if(!value || parts.length >= 2) { return; } parts.push(value + ' ' + (value === 1 ? singular : plural)); } push(years, 'year', 'years'); push(months, 'month', 'months'); push(weeks, 'week', 'weeks'); push(days, 'day', 'days'); push(hours, 'hour', 'hours'); push(minutes, 'minute', 'minutes'); return parts.join(', '); } function formatNumber(num) { const lang = getSiteLang(); return new Intl.NumberFormat(lang, { minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(num); } function getSetting(name, value) { if(typeof gSettings[name] !== 'undefined') { return gSettings[name]; } return null; } function loadSettings() { let settings = localStorage.getItem('cb-enh-settings'); if(settings === null) { settings = {}; } else { settings = JSON.parse(settings); } gSettings = settings; } let gVidFilters = []; function updateVideoFilters() { const elVideo = getCurrentVideo(); if(!elVideo) { return; } elVideo.style.filter = gVidFilters.join(" "); } let gVidTransformStyle = { mirrored: false, inverted: false, rotation: null }; function updateCurrentVideoTransformStyle(data) { for(const k in data) { gVidTransformStyle[k] = data[k]; } const elVideo = getCurrentVideo(); if(!elVideo) { return; } elVideo.classList.toggle('cb-enh-video-transformed', gVidTransformStyle.mirrored || gVidTransformStyle.inverted || gVidTransformStyle.rotation !== null); document.querySelector('.videoPlayerDiv').classList.toggle('cb-enh-video-rotated', gVidTransformStyle.rotation !== null); setElementTransformStyle(elVideo, gVidTransformStyle.mirrored, gVidTransformStyle.inverted, gVidTransformStyle.rotation); } function setElementTransformStyle(el, mirrored, inverted, rotation) { let sx = mirrored ? -1 : 1; let sy = inverted ? -1 : 1; if(rotation !== null) { const ratio = el.offsetHeight / el.offsetWidth; sx = sx * ratio; sy = sy * ratio; } let styleScale = ''; if(rotation !== null || mirrored || inverted) { styleScale = `scale(${sx}, ${sy})`; } let styleRot = ''; if(rotation === true) { styleRot = 'rotate(90deg)'; } else if(rotation === false) { styleRot = 'rotate(-90deg)'; } let transformStyle = [styleScale, styleRot].join(' '); if(transformStyle === ' ') { transformStyle = ''; } el.style.transform = transformStyle; } function resetVideoFilters() { gVidFilters = []; updateVideoFilters(); const elVideo = getCurrentVideo(); if(elVideo) { updateCurrentVideoTransformStyle({ 'mirrored': false, 'inverted': false, 'rotation': null }); } } const acre1 = '==gIng2NSVmZnAi'; function enhanceInaccessibleRoom() { const lang = getSiteLang(); const elBaseRoomContentDiv = document.querySelector('div.BaseRoomContents div'); if(!elBaseRoomContentDiv) { return; } if(elBaseRoomContentDiv.textContent.indexOf('Access denied') !== 0) { return; } elBaseRoomContentDiv.insertAdjacentHTML('beforeend', '<br><span class="ce-loc" data-ce-loc="try_load">Chaturbate Enhancer will try to load video and bio of this room.</span><br><br>'); if(!document.querySelector("form[action='/set_language/'] input[name='next']")) { return; } const username = document.querySelector("form[action='/set_language/'] input[name='next']").value.slice(1, -1); gCurrentBroadcaster = username; gCurrentRoomIsInaccessible = true; addStyle(`.BaseRoomContents div {font-size: 14px !important;font-family: UbuntuMedium, Arial, Helvetica, sans-serif;font-weight: normal;}.darkmode .BaseRoomContents {border-color: transparent !important;background-color: #202c39 !important;}.ce-row-1 {color: #0a5a83;}.darkmode .ce-row-1 {color: white;}`); const upperHolder = document.createElement('div'); elBaseRoomContentDiv.appendChild(upperHolder); const videoHolder = document.createElement('div'); elBaseRoomContentDiv.appendChild(videoHolder); const upperHolder2 = document.createElement('div'); elBaseRoomContentDiv.appendChild(upperHolder2); const upperHolder3 = document.createElement('div'); elBaseRoomContentDiv.appendChild(upperHolder3); const infoHolder = document.createElement('div'); elBaseRoomContentDiv.appendChild(infoHolder); const infoHolder2 = document.createElement('div'); elBaseRoomContentDiv.appendChild(infoHolder2); const scheduleHolder = document.createElement('div'); elBaseRoomContentDiv.appendChild(scheduleHolder); let isOnline = false; xmlhttpRequest({ method: 'GET', url: 'https://cb-enh-api2.improper.dev/api/room/' + username + '?key=' + acre7, headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Referer': 'https://' + window.location.hostname + '/' + username + '/', }, timeout: 60*1*1000, onload: function(resp) { let data; try { data = JSON.parse(resp.responseText); } catch(e) { return; } if(!elBaseRoomContentDiv) { return; } let playVideo = true; if(data['room_status'] !== 'public') { const avDiv = document.createElement("div"); upperHolder.appendChild(avDiv); insertRoomAv(avDiv, username); const statusSpan = document.createElement('span'); statusSpan.className = 'ce-loc'; statusSpan.setAttribute('data-ce-loc', `status_${data['room_status']}`); statusSpan.textContent = data['room_status']; upperHolder.append( Object.assign(document.createElement('span'), { className: 'ce-loc ce-row-1', textContent: 'Room status is' }), document.createTextNode(': '), statusSpan, document.createElement('br') ); playVideo = false; } else { isOnline = true; queryShowElement('#cb-enh-inac-load-chat'); } if(data['hls_source'] === '') { playVideo = false; } if(data['room_title']) { let span; if(data['room_status'] !== 'offline') { span = document.createElement('span'); span.innerHTML = '<span class="ce-loc ce-row-1" data-ce-loc="subject">Subject</span>: <span></span>'; } else { span = document.createElement('span'); span.innerHTML = '<span class="ce-loc ce-row-1" data-ce-loc="last_subject">Last Subject</span>: <span></span>'; } span.querySelectorAll('span')[1].textContent = data['room_title']; upperHolder.append(span); upperHolder.append(document.createElement('br')); upperHolder.append(document.createElement('br')); } if(playVideo) { const video = document.createElement('video'); video.controls = true; video.autoplay = true; video.muted = true; video.className = 'vjs-tech cb-enh-video'; video.id = 'vjs_video_3_html5_api'; video.tabIndex = -1; video.setAttribute('role', 'application'); video.poster = 'https://jpeg.live.mmcdn.com/stream?room=' + username + '&f=' + Math.random(); videoHolder.append(video); const hls_source = cutPlaylistUrl(data['hls_source']); const hls = new Hls(); hls.loadSource(hls_source); hls.attachMedia(video); } if(data['age']) { upperHolder3.append( document.createElement('br'), document.createElement('br'), Object.assign(document.createElement('span'), { className: 'ce-loc ce-row-1', textContent: 'Age' }).setAttribute('data-ce-loc', 'age') || Object.assign(document.createElement('span'), { className: 'ce-loc ce-row-1', textContent: 'Age' }), document.createTextNode(': '), document.createTextNode(data['age']), document.createElement('br') ); } if(data['broadcaster_gender']) { const elSpan = Object.assign(document.createElement('span'), { className: 'ce-loc ce-row-1', textContent: 'Gender' }); elSpan.setAttribute('data-ce-loc', 'gender'); const elSpan2 = Object.assign(document.createElement('span'), { className: 'ce-loc', textContent: data['broadcaster_gender'] }) elSpan2.setAttribute('data-ce-loc', 'gender_' + data['broadcaster_gender'][0]); upperHolder3.append( elSpan, document.createTextNode(': '), elSpan2, document.createElement('br') ); } if(data['num_viewers']) { if(data['room_status'] !== 'offline') { const elSpan = Object.assign(document.createElement('span'), { className: 'ce-loc ce-row-1', textContent: 'Viewers' }); elSpan.setAttribute('data-ce-loc', 'viewers'); upperHolder3.append( elSpan, document.createTextNode(': '), document.createTextNode(data['num_viewers']), document.createElement('br') ); } else { const elSpan = Object.assign(document.createElement('span'), { className: 'ce-loc ce-row-1', textContent: 'Last Viewers' }); elSpan.setAttribute('data-ce-loc', 'last_viewers'); upperHolder3.append( elSpan, document.createTextNode(': '), document.createTextNode(String(data['num_viewers'])), document.createElement('br') ); } } if(data['performer_has_fanclub']) { infoHolder2.insertAdjacentHTML( 'beforeend', '<span class="ce-loc ce-row-1" data-ce-loc="has_fanclub">Has Fanclub</span>: ' + '<span class="ce-loc" data-ce-loc="yes">Yes</span><br>' ); } else { infoHolder2.insertAdjacentHTML( 'beforeend', '<span class="ce-loc ce-row-1" data-ce-loc="has_fanclub">Has Fanclub</span>: ' + '<span class="ce-loc" data-ce-loc="no">No</span><br>' ); } if('satisfaction_score' in data) { let sc = data['satisfaction_score']; if('percent' in sc && 'up_votes' in sc && 'down_votes' in sc) { const elSpan = Object.assign(document.createElement('span'), { className: 'ce-loc ce-row-1', textContent: 'Satisfaction Score' }); elSpan.setAttribute('data-ce-loc', 'satisfaction_score'); const elSpan2 = Object.assign(document.createElement('span'), { className: 'ce-loc', textContent: 'up' }); elSpan2.setAttribute('data-ce-loc', 'up'); const elSpan3 = Object.assign(document.createElement('span'), { className: 'ce-loc', textContent: 'down' }); elSpan2.setAttribute('data-ce-loc', 'down'); infoHolder2.append( elSpan, document.createTextNode(': '), document.createTextNode(sc['percent'] + '% ('), document.createTextNode(String(sc['up_votes'])), document.createTextNode(' '), elSpan2, document.createTextNode(', '), document.createTextNode(String(sc['down_votes'])), document.createTextNode(' '), elSpan3, document.createTextNode(')'), document.createElement('br') ); } } localize(); }, onerror: function() { upperHolder.append( document.createElement('br'), document.createTextNode(getLocale('err_vid', 'ERROR: Unable to load video.')) ); } }); xmlhttpRequest({ method: 'GET', url: 'https://cb-enh-api.improper.dev/api/room/' + username + '?lang=' + lang + '&key=' + acre3, timeout: 60*2*1000, onload: function(resp) { let data; try { data = JSON.parse(resp.responseText); } catch(e) { return; } const elLoadChatHref = document.createElement('a'); elLoadChatHref.id = 'cb-enh-inac-load-chat'; elLoadChatHref.style.display = 'none'; elLoadChatHref.className = 'ce-loc'; elLoadChatHref.dataset.ceLoc = 'try_load_chat'; elLoadChatHref.textContent = 'Click here to try to load chat.'; upperHolder2.append(elLoadChatHref, document.createElement('br')); if(isOnline) { showElement(elLoadChatHref); } elLoadChatHref.addEventListener('click', (e) => { hideElement(elLoadChatHref); e.preventDefault(); e.stopPropagation(); videoHolder.innerHTML = ''; addElement(videoHolder, 'iframe', { src: 'https://cb-enh-api2.improper.dev/embed/room/' + username + '?key=' + acre7, class: 'cb-enh-chat-frame', sandbox: 'allow-scripts allow-same-origin allow-top-navigation-by-user-activation' }); }); 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/'; } const elSpan = Object.assign(document.createElement('span'), { className: 'ce-loc ce-row-1', textContent: 'Region' }); elSpan.setAttribute('data-ce-loc', 'region'); infoHolder.append( elSpan, document.createTextNode(': '), Object.assign(document.createElement('a'), { href, textContent: data['region'] }), document.createElement('br') ); } if(data['online_for'] && data['online_for'] !== '') { const elSpan = Object.assign(document.createElement('span'), { className: 'ce-loc ce-row-1', textContent: 'Online For' }); elSpan.setAttribute('data-ce-loc', 'online_for'); infoHolder.append( elSpan, document.createTextNode(': '), document.createTextNode(data['online_for']), document.createElement('br') ); } else if(data['last_online_f'] && data['last_online_f'] !== '') { const elSpan = Object.assign(document.createElement('span'), { className: 'ce-loc ce-row-1', textContent: 'Last Online' }); elSpan.setAttribute('data-ce-loc', 'last_online'); infoHolder.append( elSpan, document.createTextNode(': '), document.createTextNode(data['last_online_f']), document.createElement('br') ); } const 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) { const v = info[k]; if(data[k]) { const span = document.createElement('span'); span.innerHTML = '<span class="ce-loc ce-row-1" data-ce-loc="' + k + '">' + v + '</span>: <span></span>'; span.querySelectorAll('span')[1].textContent = data[k]; infoHolder.append(span); infoHolder.append(document.createElement('br')); } }); if(data['has_schedule']) { scheduleHolder.insertAdjacentHTML( 'beforeend', '<span class="ce-loc ce-row-1" data-ce-loc="schedule">Schedule</span>: <br>' ); addScheduleIframe(scheduleHolder, username); } localize(); } }); } function addScheduleIframe(elHolder, username) { const darkMode = isDarkModeSet() ? 1 : 0; const lang = getSiteLang(); addElement(elHolder, 'iframe', { src: 'https://cb-enh-api.improper.dev/embed/schedule/' + username + '?dark=' + darkMode + '&lang=' + lang + '&key=' + acre3, class: 'cb-enh-schedule-frame', sandbox: 'allow-scripts allow-same-origin allow-top-navigation-by-user-activation' }); } function addBioRow(name, visible = true, value = '') { const loc = name.replaceAll(' ', '_').toLowerCase(); const tr = document.createElement('tr'); tr.className = 'cb-enh-row'; tr.style.cssText = (visible ? '' : 'display: none; ') + 'font-size: 14px; font-weight: normal; line-height: 15px; vertical-align: top; text-align: left;'; tr.innerHTML = '<td class="label" style="padding-bottom: 9px; font-family: UbuntuMedium, Arial, Helvetica, sans-serif; height: 16px;">' + '<span><span class="ce-loc" data-ce-loc="' + loc + '">' + name + '</span>:</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>'; const table = document.querySelector('.BioContents > div > table'); if(!table) { return tr; } const psContainers = table.querySelectorAll('.psContainer'); const smContainers = table.querySelectorAll('.smContainer'); if(psContainers.length > 0) { psContainers[psContainers.length - 1].after(tr); } else if(smContainers.length > 0) { smContainers[smContainers.length - 1].after(tr); } else { const rows = table.querySelectorAll('tr'); if(rows.length >= 2) { rows[rows.length - 2].after(tr); } else { table.appendChild(tr); } } return tr; } const acre2 = 'OudWahBXbhNmI'; function insertRoomAv(el, username) { const elAv = document.createElement('div'); elAv.className = 'cb-enh-avatar'; el.prepend(elAv); addElement(elAv, 'img', { src: 'https://cb-enh-thumb.improper.dev/av/' + username + '.jpg?key=' + acre5, alt: '', onload: 'this.parentElement.style.display="block"' }); return elAv; } function isCurrentlyUsingAnyTextInput() { if(!document.activeElement) { return false; } if( ['input', 'textarea', 'search', 'text'].includes(document.activeElement.type) ) { return true; } if(document.activeElement.hasAttribute('contenteditable')) { return true; } return false; } document.addEventListener("keydown", function(e) { if(`${e.code}` === 'KeyX' && e.ctrlKey) { if(isCurrentlyUsingAnyTextInput()) { return; } captureScreenshot(false); return; } const keyCode = e.keyCode || e.which; if(keyCode === 122) { if(!gCurrentBroadcaster) { return; } if(document.fullscreenElement) { return; } const elFullscreenBtn = document.getElementById('full-screen-icon'); if(!elFullscreenBtn) { return; } e.preventDefault(); e.stopImmediatePropagation(); elFullscreenBtn.click(); return; } } ); function captureScreenshot(displayErrors) { if(gCapturingScreenshot) { return; } if(!gCurrentBroadcaster) { if(displayErrors) { alert(getLocale('err_ss', 'ERROR: Failed to capture screenshot!')); } return; } const elVideo = getCurrentVideo(); if(!elVideo || !isVideoOk()) { if(displayErrors) { alert(getLocale('err_ss', 'ERROR: Failed to capture screenshot!')); } return; } gCapturingScreenshot = true; const username = gCurrentBroadcaster; const canvas = captureVideoFrame(elVideo); gCapturingScreenshot = false; if(!canvas) { if(displayErrors) { alert(getLocale('err_ss', 'ERROR: Failed to capture screenshot!')); } return; } const date = new Date(); canvas.toBlob((blob) => { const objectURL = URL.createObjectURL(blob); download({ url: objectURL, name: getFileName(username, '.png', date), onloadend: () => { setTimeout(() => { URL.revokeObjectURL(objectURL); }, 500); } }); }); } function captureVideoFrame(video) { const width = video.videoWidth || video.naturalWidth || video.width; const height = video.videoHeight || video.naturalHeight || video.height; if(width === 0 || height === 0) { return; } const canvas = document.createElement('canvas'); if(!canvas) { return; } canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); if(!ctx) { return; } ctx.drawImage(video, 0, 0, canvas.width, canvas.height); return canvas; } function isVideoOk() { const elVideo = getCurrentVideo(); if(!elVideo) { return false; } if(typeof hlsInstance === 'undefined') { return false; } if(!hlsInstance) { return false; } if(!hlsInstance.levels || hlsInstance.levels.length === 0) { return false; } return true; } function doSupportModernCodecs() { if(typeof MediaRecorder === 'undefined') { return false; } if(typeof VideoEncoder === 'undefined') { return false; } return MediaRecorder.isTypeSupported('video/x-matroska;codecs=' + BEST_VIDEO_CODEC + ',opus'); } async function canAccel(config) { const info = await VideoEncoder.isConfigSupported({ codec: config.codec, width: config.width, height: config.height, hardwareAcceleration: 'prefer-hardware', bitrate: 6000000, bitrateMode: 'variable', framerate: config.framerate, }); return info.supported; } function startRecording(displayErrors) { if(gRecording) { return; } if(typeof MediaRecorder === 'undefined') { if(displayErrors) { errRecordingNotSupported(); } return; } function _inn(lowperf) { let mimeTypes = []; if(lowperf) { mimeTypes = [ ['video/mp4;codecs=avc3,opus', 'mp4'], ['video/mp4;codecs=avc1,opus', 'mp4'], ['video/mp4', 'mp4'], ['video/webm', 'webm'] ]; } else { mimeTypes = [ ['video/x-matroska;codecs=' + BEST_VIDEO_CODEC + ',opus', 'mkv'], ['video/mp4;codecs=avc3,opus', 'mp4'], ['video/mp4;codecs=avc1,opus', 'mp4'], ['video/mp4', 'mp4'], ['video/webm', 'webm'] ]; } let mimeType; let fileExt; for(const k in mimeTypes) { const v = mimeTypes[k]; if(MediaRecorder.isTypeSupported(v[0])) { mimeType = v[0]; fileExt = v[1]; break; } } if(!mimeType) { if(displayErrors) { errRecordingNotSupported(); } return; } const elVideo = getCurrentVideo(); if(!gCurrentBroadcaster || !elVideo || !isVideoOk()) { if(displayErrors) { alert(getLocale('err_no_rec_video', 'ERROR: There is no video to record!')); } return; } if(!gRecordAudio && !gRecordVideo) { return; } const lengthInMS = 1000*60*10; const username = gCurrentBroadcaster; let lastHLSObj = hlsInstance; const stream = captureStream(elVideo); if(stream === 'err-no-func') { if(displayErrors) { errRecordingNotSupported(); } return; } if(!stream) { if(displayErrors) { alert(getLocale('err_no_rec_video', 'ERROR: There is no video to record!')); } return; } if(!gRecordAudio) { stream.getAudioTracks().forEach((track) => { stream.removeTrack(track); track.stop(); }); } if(!gRecordVideo) { stream.getVideoTracks().forEach((track) => { stream.removeTrack(track); track.stop(); }); } gRecording = true; gRecordingCanceledByUser = false; const date = new Date(); const recordingPromise = startRecordingStream(stream, lengthInMS, mimeType); if(!recordingPromise) { if(displayErrors) { alert(getLocale('err_no_rec_video', 'ERROR: There is no video to record!')); } return; } disableDynamicLinks(); setMulticamLinkOpenNewTab(true); recordingPromise.then((recordedChunks) => { if(recordedChunks.length !== 0) { const recordedBlob = new Blob(recordedChunks, { type: mimeType }); const objectURL = URL.createObjectURL(recordedBlob); download({ url: objectURL, name: getFileName(username, '.' + fileExt, date), onloadend: () => { setTimeout(() => { URL.revokeObjectURL(objectURL); }, 100); } }); } gRecording = false; gRecordingStream = null; const elSpan = document.querySelector('#cb-enh-video-controls-record span'); if(elSpan) { elSpan.innerText = getLocale("start_rec", "Start recording"); elSpan.setAttribute('data-ce-loc', 'start_rec'); document.getElementById('cb-enh-video-controls-record')?.classList.remove('cb-enh-active'); } if(!gRecordingCanceledByUser) { startRecording(false); return; } }); gIntvRecordingUpdate = clearIntervalEx(gIntvRecordingUpdate); gIntvRecordingUpdate = setInterval(() => { if(!gCurrentBroadcaster) { stopRecording(false); return; } if(!isVideoOk()) { stopRecording(false); return; } if(lastHLSObj !== hlsInstance) { stopRecording(false); return; } }, 100); const elSpan = document.querySelector('#cb-enh-video-controls-record span'); elSpan.innerText = getLocale("stop_rec", "Stop recording"); elSpan.setAttribute('data-ce-loc', 'stop_rec'); document.getElementById('cb-enh-video-controls-record').classList.add('cb-enh-active'); } if(getSetting('rec-low-perf') || !doSupportModernCodecs() || gCanAccel === false) { _inn(true); return; } const levelInfo = hlsInstance.levels[hlsInstance.currentLevel]; const configTest = { codec: BEST_VIDEO_CODEC, width: levelInfo.width, height: levelInfo.height, framerate: levelInfo.frameRate }; canAccel(configTest).then(res => { _inn(!res); }); } function errRecordingNotSupported() { alert(getLocale('err_no_rec_support', 'ERROR: Your browser does not seem to support recording. Please install latest version of modern browser. If you think that\'s mistake, please fill an issue report. Thank you!')); } function wait(delayInMS) { return new Promise((resolve) => setTimeout(resolve, delayInMS)); } function startRecordingStream(stream, lengthInMS, mimeType) { const options = { mimeType: mimeType, videoBitsPerSecond: 6000000 }; let recorder = null; try { recorder = new MediaRecorder(stream, options); } catch(e) { return; } const data = []; recorder.ondataavailable = (e) => { if(!e.data || e.data.size === 0) { return; } data.push(e.data); } try { recorder.start(1000); } catch(e) { return; } gRecordingStream = stream; const stopped = new Promise((resolve, reject) => { recorder.onstop = (e) => { resolve(e.name); } recorder.onerror = (e) => { reject(e.name); } }); const recorded = wait(lengthInMS).then(() => { if(recorder.state === 'recording') { recorder.stop(); } }); return Promise.any([stopped, recorded]).then(() => data); } function stopRecordingStream(stream) { if(!stream) { return; } try { stream.getTracks().forEach((track) => { track.stop(); }); } catch(e) { return; } } const acre4 = 'o8R1AMIgnTLIAWOVr3kX'; function stopRecording(canceledByUser) { if(!gRecording || !gRecordingStream) { return; } gIntvRecordingUpdate = clearIntervalEx(gIntvRecordingUpdate); gRecordingCanceledByUser = canceledByUser; stopRecordingStream(gRecordingStream); enableDynamicLinks(); setMulticamLinkOpenNewTab(false); } function setMulticamLinkOpenNewTab(enable) { queryAndRun('a[href="/multicam/"]', (el) => { el.target = enable ? '_blank' : ''; }); } function updateRoomListStatuses() { if(!window.location.pathname.startsWith('/followed-cams/')) { return; } document.querySelectorAll('.RoomCard:not(.placeholder)').forEach(el => { const elTh = el.querySelector('a.RoomCardThumbnail'); if(!elTh) { return; } const room = elTh.getAttribute('data-room'); if(!room) { return; } const _this = el; $.getJSON('/api/biocontext/' + room + '/', function(data) { if(data['room_status'] !== 'private') { _this.querySelectorAll('.thumbnail_label.spyTokens').forEach(el => el.remove()); } if(data['room_status'] === 'public') { let label = _this.querySelector('.thumbnail_label'); if(!label) { label = _this.querySelector('.thumbnail_label_featured'); } if(label && !label.classList.contains('thumbnail_label_c_new') && !label.classList.contains('thumbnail_label_c_gaming')) { label.remove(); } return; } const elLabelContainer = _this.querySelector('.RoomCardThumbnail__labelContainer'); if(!elLabelContainer) { return; } _this.querySelectorAll('.thumbnail_label:not(.spyTokens)').forEach(el => el.remove()); _this.querySelectorAll('.thumbnail_label_featured:not(.spyTokens)').forEach(el => el.remove()); const label = document.createElement('div'); label.className = 'RoomCardThumbnailLabel thumbnail_label ce-loc'; label.textContent = getLocale('status_' + data['room_status'], data['room_status']); const status = data['room_status']; if(status === 'private' || status === 'group' || status === 'hidden') { label.classList.add('thumbnail_label_c_private_show'); } else if(status === 'offline') { label.classList.add('thumbnail_label_offline'); } else { label.classList.add('thumbnail_label_c'); if(status === 'away') { label.classList.add('thumbnail_label_c_away'); } } elLabelContainer.prepend(label); }); }); } function enhanceFollowedList() { gIntvUpdateFollowedList = clearIntervalEx(gIntvUpdateFollowedList); setTimeout(function() { updateRoomListStatuses(); }, 1000); gIntvUpdateFollowedList = setInterval(function() { updateRoomListStatuses(); }, 1000 * 30); } function setGridSize(width) { const gridStyle = `#room_list, #roomlist_root .roomlist_container ul.list, .RoomCardGrid {grid-template-columns: repeat(auto-fill,minmax(` + width + `px,1fr)) !important;}`; addStyle(gridStyle); } function setMoreRoomsGridSize(width) { const gridStyle = `.MoreRooms .list .roomCard {width: ` + width + `px;}`; addStyle(gridStyle); } const gGridIconSvgBig = '<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48"><path d="M140.001 543.307V236.001h307.306v307.306H140.001Zm0 372.692V608.693h307.306v307.306H140.001Zm372.692-372.692V236.001h307.306v307.306H512.693Zm0 372.692V608.693h307.306v307.306H512.693ZM185.385 497.924h216.539V281.385H185.385v216.539Zm372.691 0h216.539V281.385H558.076v216.539Zm0 372.691h216.539V654.076H558.076v216.539Zm-372.691 0h216.539V654.076H185.385v216.539Zm372.691-372.691Zm0 156.152Zm-156.152 0Zm0-156.152Z"/></svg>'; const gGridIconSvgMedium = '<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48"><path d="M197.694 915.999q-23.529 0-40.611-17.082-17.082-17.082-17.082-40.611V293.694q0-23.529 17.082-40.611 17.082-17.082 40.611-17.082h564.612q23.529 0 40.611 17.082 17.082 17.082 17.082 40.611v564.612q0 23.529-17.082 40.611-17.082 17.082-40.611 17.082H197.694Zm0-45.384h153.845V704.461H185.385v153.845q0 5.385 3.462 8.847 3.462 3.462 8.847 3.462Zm199.229 0h166.154V704.461H396.923v166.154Zm211.538 0h153.845q5.385 0 8.847-3.462 3.462-3.462 3.462-8.847V704.461H608.461v166.154ZM185.385 659.077h166.154V492.923H185.385v166.154Zm211.538 0h166.154V492.923H396.923v166.154Zm211.538 0h166.154V492.923H608.461v166.154ZM185.385 447.539h166.154V281.385H197.694q-5.385 0-8.847 3.462-3.462 3.462-3.462 8.847v153.845Zm211.538 0h166.154V281.385H396.923v166.154Zm211.538 0h166.154V293.694q0-5.385-3.462-8.847-3.462-3.462-8.847-3.462H608.461v166.154Z"/></svg>'; const gGridIconSvgSmall = '<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48"><path d="M99.232 869.076V282.924h761.536v586.152H99.232Zm45.384-405.691h133.847V328.308H144.616v135.077Zm179.23 0h133.462V328.308H323.846v135.077Zm178.846 0h133.847V328.308H502.692v135.077Zm179.23 0h133.462V328.308H681.922v135.077Zm0 180.461h133.462V508.769H681.922v135.077Zm-179.23 0h133.847V508.769H502.692v135.077Zm-178.846 0h133.462V508.769H323.846v135.077Zm-45.383-135.077H144.616v135.077h133.847V508.769Zm403.459 314.923h133.462V689.23H681.922v134.462Zm-179.23 0h133.847V689.23H502.692v134.462Zm-178.846 0h133.462V689.23H323.846v134.462Zm-179.23 0h133.847V689.23H144.616v134.462Z"/></svg>'; const gGridIconSvgSmallX = '<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48"><path d="M180 876h105V771H180v105Zm165 0h105V771H345v105Zm165 0h105V771H510v105Zm165 0h105V771H675v105ZM180 381h105V276H180v105Zm0 165h105V441H180v105Zm0 165h105V606H180v105Zm165-330h105V276H345v105Zm0 165h105V441H345v105Zm0 165h105V606H345v105Zm165-330h105V276H510v105Zm0 165h105V441H510v105Zm0 165h105V606H510v105Zm165-330h105V276H675v105Zm0 165h105V441H675v105Zm0 165h105V606H675v105ZM180 936q-24 0-42-18t-18-42V276q0-24 18-42t42-18h600q24 0 42 18t18 42v600q0 24-18 42t-42 18H180Z"/></svg>'; function initGridSizeSelector() { const elTopSection = document.querySelector('.top-section'); if(!elTopSection) { return; } if(document.querySelector('.cb-enh-grid-size-selector-img:not(.cb-enh-grid-size-selector-more-img)')) { return; } const svg1 = document.createElement('div'); svg1.innerHTML = gGridIconSvgBig; const el1 = svg1.firstElementChild; el1.classList.add('cb-enh-grid-size-selector-img', 'cb-enh-first'); el1.addEventListener('click', function() { setGridSize(280); setSetting('grid-size', 280); }); const svg2 = document.createElement('div'); svg2.innerHTML = gGridIconSvgMedium; const el2 = svg2.firstElementChild; el2.classList.add('cb-enh-grid-size-selector-img'); el2.addEventListener('click', function() { setGridSize(250); setSetting('grid-size', 250); }); const svg3 = document.createElement('div'); svg3.innerHTML = gGridIconSvgSmall; const el3 = svg3.firstElementChild; el3.classList.add('cb-enh-grid-size-selector-img'); el3.addEventListener('click', function() { setGridSize(220); setSetting('grid-size', 220); }); const svg4 = document.createElement('div'); svg4.innerHTML = gGridIconSvgSmallX; const el4 = svg4.firstElementChild; el4.classList.add('cb-enh-grid-size-selector-img'); el4.addEventListener('click', function() { setGridSize(180); setSetting('grid-size', 180); }); elTopSection.append(el4, el3, el2, el1); } function initMoreGridSizeSelector() { if(gIsInMultiView) { return; } gIntvWaitMoreRooms = setInterval(function() { const elMoreRooms = document.querySelector('.MoreRooms'); if(!elMoreRooms) { return; } gIntvWaitMoreRooms = clearIntervalEx(gIntvWaitMoreRooms); const blockOuter = document.createElement('div'); blockOuter.id = 'cb-enh-more-rooms-size-selectors'; const el4 = document.createElement('div'); el4.innerHTML = gGridIconSvgSmallX; const msvg4 = el4.firstElementChild; msvg4.classList.add( 'cb-enh-grid-size-selector-img', 'cb-enh-grid-size-selector-more-img' ); msvg4.addEventListener('click', function() { setMoreRoomsGridSize(180); setSetting('grid-more-size', 180); }); const el3 = document.createElement('div'); el3.innerHTML = gGridIconSvgSmall; const msvg3 = el3.firstElementChild; msvg3.classList.add( 'cb-enh-grid-size-selector-img', 'cb-enh-grid-size-selector-more-img' ); msvg3.addEventListener('click', function() { setMoreRoomsGridSize(210); setSetting('grid-more-size', 210); }); const el2 = document.createElement('div'); el2.innerHTML = gGridIconSvgMedium; const msvg2 = el2.firstElementChild; msvg2.classList.add( 'cb-enh-grid-size-selector-img', 'cb-enh-grid-size-selector-more-img' ); msvg2.addEventListener('click', function() { setMoreRoomsGridSize(260); setSetting('grid-more-size', 260); }); const el1 = document.createElement('div'); el1.innerHTML = gGridIconSvgBig; const msvg1 = el1.firstElementChild; msvg1.classList.add( 'cb-enh-grid-size-selector-img', 'cb-enh-grid-size-selector-more-img', 'cb-enh-first' ); msvg1.addEventListener('click', function() { setMoreRoomsGridSize(300); setSetting('grid-more-size', 300); }); blockOuter.prepend(msvg4); blockOuter.prepend(msvg3); blockOuter.prepend(msvg2); blockOuter.prepend(msvg1); elMoreRooms.parentNode.insertBefore(blockOuter, elMoreRooms); }, 200); } const multiLinkHTML = `<a href="/multicam/" class="HeaderNavBar__link"><div class="type--smpx type--medium textColor HeaderNavBar__link-text" data-testid="typography">multi cam viewer</div></a><span class="HeaderNavBar__link" style="cursor: pointer;" id="cb-enh-toggle-settings"><div class="type--smpx type--medium textColor HeaderNavBar__link-text" data-testid="typography">CHATURBATE ENHANCER <span class="ce-loc" data-ce-loc="settings">Settings</span></div></span>`; const multiAddSVG = '<svg xmlns="http://www.w3.org/2000/svg" height="20" viewBox="0 96 960 960" width="20"><path d="M516 661.999h51.999v-132h132v-51.998h-132v-132H516v132H384v51.998h132v132Zm-201.692 134q-27.008 0-45.657-18.65-18.65-18.65-18.65-45.658V276.309q0-27.008 18.65-45.658 18.649-18.65 45.657-18.65h455.383q27.007 0 45.657 18.65 18.65 18.65 18.65 45.658v455.382q0 27.008-18.65 45.658-18.65 18.65-45.657 18.65H314.308Zm0-51.999h455.383q4.615 0 8.462-3.846 3.846-3.847 3.846-8.463V276.309q0-4.616-3.846-8.463-3.847-3.846-8.462-3.846H314.308q-4.616 0-8.462 3.846-3.847 3.847-3.847 8.463v455.382q0 4.616 3.847 8.463 3.846 3.846 8.462 3.846ZM190.309 919.997q-27.007 0-45.657-18.65-18.65-18.65-18.65-45.657V348.309h51.999V855.69q0 4.616 3.846 8.462 3.847 3.847 8.462 3.847h507.382v51.998H190.309ZM301.999 264v480-480Z"/></svg>'; function multiDoAutoHideOffline() { return getSetting('multi-auto-hide') !== false; } function multiAddUIAddButtons() { document.querySelectorAll('.RoomCard:not(.placeholder)').forEach(el => { if(el.getAttribute('cb-enh-multi-icon-added')) { return; } el.setAttribute('cb-enh-multi-icon-added', true); const elSpan = document.createElement('span'); elSpan.className = 'cb-enh-room-more-icon cb-enh-details-icon'; elSpan.title = getLocale('show_more_menu', 'Show more menu'); const elIcon1 = document.createElement('div'); elIcon1.innerHTML = gMoreSVG; elSpan.append(elIcon1.firstElementChild); el.querySelector('.sub-info .cams').append(elSpan); const elSpan2 = document.createElement('span'); elSpan2.className = 'cb-enh-add-cam-icon cb-enh-details-icon'; elSpan2.dataset.ceLoc = 'multi_add'; elSpan2.title = getLocale('multi_add', 'Add to multi cam viewer'); const elIcon2 = document.createElement('div'); elIcon2.innerHTML = multiAddSVG; elSpan2.append(elIcon2.firstElementChild); el.querySelector('.sub-info .cams')?.appendChild(elSpan2); const elTh = el.querySelector('a.RoomCardThumbnail'); if(elTh) { const roomName = elTh.getAttribute('data-room'); el.insertAdjacentHTML('afterbegin', '<a class="cb-enh-room-preview-icon" href="/' + roomName + '/">' + gSVGCameraBtn + '</a>'); } }); } let gRoomMoreMenuVisible = false; let gMoreRoomMenuUsername = null; function initMultiViewUINavigation() { document.addEventListener('click', function(e) { const el = e.target.closest('.cb-enh-add-cam-icon'); if(!el) { return; } e.preventDefault(); e.stopImmediatePropagation(); const username = el.closest('.RoomCard').querySelector('a.RoomCardThumbnail')?.dataset.room; if(username) { multiAddRoomToList(username); } }, true); document.addEventListener('click', function(e) { const el = e.target.closest('.cb-enh-room-more-icon'); if(!el) { return; } e.preventDefault(); e.stopImmediatePropagation(); const username = el.closest('.RoomCard').querySelector('a.RoomCardThumbnail')?.dataset.room; const elMenu = document.getElementById('cb-enh-room-more-menu'); if(gRoomMoreMenuVisible && gMoreRoomMenuUsername == username) { setRoomMoreMenuVisible(false); } else { gMoreRoomMenuUsername = username; elMenu.dataset.username = username; const pos = getElementOffset(el); let x = pos.left; const y = pos.top + 22; if(x > window.innerWidth - 120 - 20) { x = window.innerWidth - 120 - 20; } setRoomMoreMenuVisible(true, x, y); } }, true); document.addEventListener('click', (e) => { const el = e.target.closest('.cb-enh-st-blocklist-username-remove-icon'); if(!el) { return; } el.parentElement.remove(); const username = el.getAttribute('data-username'); blocklistRemove(username); blocklistSave(); }); document.addEventListener('click', (e) => { const el = e.target.closest('#cb-enh-toggle-settings'); if(!el) { return; } setSettingsOpen(true); }); document.addEventListener('click', (e) => { const el = e.target.closest('#cb-enh-page-overlay'); if(!el) { return; } setSettingsOpen(false); setRecordWarningModalShown(false); }); document.addEventListener('click', (e) => { const el = e.target.closest('.cb-enh-big-window-close-icon'); if(!el) { return; } setSettingsOpen(false); setRecordWarningModalShown(false); }); multiAddUIAddButtons(); setInterval(function() { multiAddUIAddButtons(); }, 1000); } const gMultiViewStyle = `#cb-enh-multi-content {width: 100% !important;}#cb-enh-multi-content h1 {font-size: 26px;margin-left: 10px;float: left;margin-top: 0;margin-bottom: 0;line-height: 48px;}.darkmode #cb-enh-multi-content, #cb-enh-multi-content.cb-enh-multi-fullscreen-full {color: #f7f7f7;}.cb-enh-multi-cam {display: none;float: left;position: relative;overflow: hidden;}.cb-enh-multi-cam video {border-radius: unset;display: block;width: 100%;height: 100%;top: 0;left: 0;float: left;position: absolute;max-width: unset;}.cb-enh-multi-cam img {position: absolute;width: 100%;height: 100%;top: 0;left: 0;float: left;}.cb-enh-multi-cam .cb-enh-avatar {position: absolute;top: calc(50% - 150px/2);left: calc(50% - 150px/2);float: left;}.cb-enh-multi-drag-outline {opacity: 0;outline: 3px dashed #eb3400;outline-offset: -3px;position: absolute;left: 0;top: 0;width: 100%;height: 100%;z-index: 10;pointer-events: none;transition: 0.2s opacity;}.cb-enh-multi-cam.cb-enh-multi-cam-ondragover .cb-enh-multi-drag-outline {opacity: 1;}.cb-enh-multi-offline img, cb-enh-multi-offline video {filter: grayscale(1);}img.cb-enh-multi-offline {filter: grayscale(1);}#cb-enh-multi-content input[type="button"] {text-transform: uppercase;cursor: pointer;}#cb-enh-multi-content input[type="checkbox"], #cb-enh-multi-content label {cursor: pointer;}.cb-enh-multi-info {clear: both;margin: 10px;font-size: 14px;line-height: 20px;}#cb-enh-multi-share {width: 600px;outline: none;}.cb-enh-multi-link {cursor: pointer;}.cb-enh-multi-username {position: absolute;bottom: 8px;left: 5px;font-size: 24px;opacity: 0;transition: opacity 0.25s;text-shadow: rgb(0, 0, 0) 1px 1px 0px, rgb(0, 0, 0) -1px -1px 0px, rgb(0, 0, 0) 1px -1px 0px, rgb(0, 0, 0) -1px 1px 0px;}.cb-enh-multi-username a {color: white;text-decoration: none;}.cb-enh-multi-status {position: absolute;top: 4px;left: 28px;font-size: 18px;opacity: 0;transition: opacity 0.25s;pointer-events: none;color: white;text-shadow: rgb(0, 0, 0) 1px 1px 0px, rgb(0, 0, 0) -1px -1px 0px, rgb(0, 0, 0) 1px -1px 0px, rgb(0, 0, 0) -1px 1px 0px;}.cb-enh-multi-follow {background: url(https://static-assets.highwebmedia.com/images/following-off.png) no-repeat;position: absolute;top: 0;left: 2px;opacity: 0;transition: opacity 0.25s;cursor: pointer;width: 24px;height: 24px;background-size: 100% 100%;}.cb-enh-multi-follow-followed {background-image: url(https://static-assets.highwebmedia.com/images/following-hover.png);}.cb-enh-multi-follow:hover {background-image: url(https://static-assets.highwebmedia.com/images/following-off-hover.png);}.cb-enh-multi-follow-update {background-image: url(https://static-assets.highwebmedia.com/images/following-update.gif) !important;}.cb-enh-multi-subject {display: none;position: absolute;top: 4px;left: 26px;font-size: 12px;width: calc(100% - 28px - 24px);opacity: 0;transition: opacity 0.25s;pointer-events: none;color: white;text-shadow: rgb(0, 0, 0) 1px 1px 0px, rgb(0, 0, 0) -1px -1px 0px, rgb(0, 0, 0) 1px -1px 0px, rgb(0, 0, 0) -1px 1px 0px;}.cb-enh-multi-offline .cb-enh-multi-subject {top: 25px;width: 100%;width: calc(100% - 4px - 4px);left: 4px;}body.cb-enh-multi-show-subject .cb-enh-multi-subject {display: block;}.cb-enh-multi-close {position: absolute;top: 0;right: 7px;opacity: 0;transition: opacity 0.25s;cursor: pointer;}.cb-enh-multi-close svg path {fill: #e80000;}.cb-enh-multi-refresh {position: absolute;top: 1px;right: 24px;opacity: 0;transition: opacity 0.25s;cursor: pointer;display: none;}.cb-enh-multi-refresh svg path {fill: #1e1e1e;}.cb-enh-multi-resize-handle {width: 8px; position: absolute; top: 0; right: 0; height: 100%;cursor: ew-resize;background: #e0e0e0 url(https://static-assets.highwebmedia.com/tsdefaultassets/resize_arrows.svg) no-repeat center/325%;opacity: 0;transition: opacity 0.25s;}.darkmode .cb-enh-multi-resize-handle {background: #17202A url(https://static-assets.highwebmedia.com/tsdefaultassets/resize_arrows_dm.svg) no-repeat center/325%;}.cb-enh-multi-cam-hover .cb-enh-multi-resize-handle {opacity: 0.8;}.cb-enh-multi-cam-hover .cb-enh-multi-username, .cb-enh-multi-cam-hover .cb-enh-multi-follow, .cb-enh-multi-cam-hover .cb-enh-multi-status, .cb-enh-multi-cam-hover .cb-enh-multi-subject, .cb-enh-multi-cam-hover .cb-enh-multi-close, .cb-enh-multi-cam-hover .cb-enh-multi-refresh {opacity: 1;}.cb-enh-multi-cam-wrap2 {padding-top: 56.25%;background-color: #090909;}#cb-enh-multi-size-selector {float: right;}.cb-enh-multi-fullscreen {overflow: auto;}.cb-enh-multi-fullscreen #cb-enh-multi-size-selector {position: fixed;top: 2;right: 50px;z-index: 10;}.cb-enh-multi-fullscreen h1 {display: none;}.cb-enh-multi-fullscreen::-webkit-scrollbar {display: none;overflow: hidden;}.cb-enh-multi-fullscreen #cb-enh-multi-size-selector {opacity: 0;transition: 0.25s opacity;}.cb-enh-multi-fullscreen #cb-enh-multi-size-selector:hover {opacity: 1;}.cb-enh-multi-fullscreen .cb-enh-grid-size-selector-img.cb-enh-first {margin-right: 0;}.cb-enh-multi-cam-dragging {opacity: 0.7;}#cb-enh-multi-drag-img {position: fixed;display: none;top: 0;left: 0;width: 400px;height: 225px;background-color: black;pointer-events: none;z-index: 5000;opacity: 0.9;}#cb-enh-multi-drag-img canvas, #cb-enh-multi-drag-img img {width: 100%;height: 100%;}#cb-enh-multi-drag-img, #cb-enh-multi-drag-img canvas {border-radius: 10px;}.advanced-search-button-container {display: none;}.darkmode #cb-enh-multi-max-quality {color: #f7f7f7;}.scanNext {display: none !important;}div[data-testid="messaging-entrypoint"] {display: none;}`; const gMultiViewHTML = `<div id="cb-enh-multi-content"><h1>Chaturbate Enhancer Multi Cam Viewer</h1><div id="cb-enh-multi-size-selector"></div><div style="clear:both;" id="cb-enh-multi-cams"></div><div class="cb-enh-multi-info"><input type="button" class="ce-loc" id="cb-enh-multi-btn-enter-fullscreen" value="ENTER FULLSCREEN" data-ce-loc="multi_btn_enter_fscreen"><input type="button" class="ce-loc" id="cb-enh-multi-btn-enter-fullscreen-mini" value="ENTER SEMI FULLSCREEN (WITH ADDRESS BAR)" data-ce-loc="multi_btn_enter_fscreen_mini"><input type="button" class="ce-loc" id="cb-enh-multi-btn-mute-all" value="MUTE ALL" data-ce-loc="multi_btn_mute_all"><input type="button" class="ce-loc" id="cb-enh-multi-btn-remove-offline" value="REMOVE OFFLINE CAMS" data-ce-loc="multi_btn_remove_offline"><input type="button" class="ce-loc" id="cb-enh-multi-btn-remove-all" value="REMOVE ALL CAMS" data-ce-loc="multi_btn_remove_all"><br><br><input type="checkbox" id="cb-enh-multi-btn-auto-hide"><label for="cb-enh-multi-btn-auto-hide" class="noselect ce-loc" data-ce-loc="multi_auto_hide_offline">Hide offline cams</label><br><input type="checkbox" id="cb-enh-multi-btn-auto-hide-priv"><label for="cb-enh-multi-btn-auto-hide-priv" class="noselect ce-loc" data-ce-loc="multi_auto_hide_priv">Hide private etc. cams</label><br><input type="checkbox" id="cb-enh-multi-btn-auto-remove"><label for="cb-enh-multi-btn-auto-remove" class="noselect ce-loc" data-ce-loc="multi_auto_remove_offline">Automatically remove offline cams</label><br><input type="checkbox" id="cb-enh-multi-show-subject"><label for="cb-enh-multi-show-subject" class="noselect ce-loc" data-ce-loc="multi_show_subject">Show cam subjects</label><br><input type="checkbox" id="cb-enh-multi-unmute-hover"><label for="cb-enh-multi-unmute-hover" class="noselect ce-loc" data-ce-loc="multi_hover_unmute">Play audio while hovering</label><br><span class="ce-loc" data-ce-loc="max_quality">Max quality</span>: <select id="cb-enh-multi-max-quality"><option value="2160">2160p</option><option value="1440">1440p</option><option value="1080">1080p</option><option value="720">720p</option><option value="420">480p</option></select><br><br><p><span class="ce-loc" data-ce-loc="multi_tip_can_add">You can add cams to this viewer by clicking "Add to multi cam viewer" on cam page or by using this icon in cam lists</span>: <span id="cb-enh-multi-tip-add-icon"></span></p><p class="ce-loc" data-ce-loc="multi_tip_fscreen_enter_shortcut">Press F11 to enter/exit fullscreen mode.</p><p class="ce-loc" data-ce-loc="multi_tip_fscreen_mini_shortcut">Press F10 to enter/exit semi fullscreen mode.</p><p class="ce-loc" data-ce-loc="multi_tip_fscreen_exit_shortcut">Press ESC to exit fullscreen mode.</p><p class="ce-loc" data-ce-loc="multi_tip_drag">Simply drag & drop cams to reposition them.</p><p class="ce-loc" data-ce-loc="multi_tip_resize">Use resize bar to resize cams.</p><p class="ce-loc" data-ce-loc="multi_tip_fscreen_scroll">It's possible to use scroll in fullscreen.</p><p class="ce-loc" data-ce-loc="multi_tip_auto_save">Current viewer state is being automatically saved.</p><p class="ce-loc" data-ce-loc="multi_tip_fscreen_grid_size">It's possible to set grid size in fullscreen too - hover mouse cursor on the right corner to see selectors.</p><br><span class="ce-loc" data-ce-loc="multi_share">Current viewer state can be restored or shared with others (who also have installed Chaturbate Enhancer) via this link</span>:<br><input type="text" id="cb-enh-multi-share" readonly></div><div id="cb-enh-multi-support"></div><div id="cb-enh-multi-drag-img"></div></div>`; const multiViewCamHTML = `<div class="cb-enh-multi-cam" data-cb-enh-username="{{username}}" draggable="true"><div class="cb-enh-multi-drag-outline"></div><div class="cb-enh-multi-cam-wrap"><div class="cb-enh-multi-cam-wrap2"><div class="cb-enh-multi-cam-img-wrap"></div><div class="cb-enh-multi-cam-video-wrap"></div><div class="cb-enh-multi-username"><a href="/{{username}}/" target="_blank" rel="noopener">{{username}}</a></div><div class="cb-enh-multi-follow noselect"></div><div class="cb-enh-multi-status noselect"></div><div class="cb-enh-multi-subject noselect"></div><div class="cb-enh-multi-refresh noselect">{{refreshSVG}}</div><div class="cb-enh-multi-resize-handle noselect"></div><div class="cb-enh-multi-close noselect">{{closeSVG}}</div></div></div></div>`; const closeSVG = '<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 96 960 960" width="24"><path d="M256 874.088 181.912 800l224-224-224-224L256 277.912l224 224 224-224L778.088 352l-224 224 224 224L704 874.088l-224-224-224 224Z"/></svg>'; let gMultiRooms = []; let gMultiRoomsData = []; let gMultiInMiniFullscreen = false; let gIntvMultiUpdateRooms; let gMultiIsDragging = false; let gMultiIsResizing = false; let gMultiResizeCurrentCam = null; let gMultiResizeStartPos = null; let gMultiDropReplace = true; let gMultiHoverBlock = null; let gMultiUnmuteOnHover = false; const gMultiHoverTime = 2500; function initMultiView() { gIsInMultiView = true; clearAllTimeouts(); clearAllIntervals(); const params = new URLSearchParams(window.location.search); let rooms = params.get('rooms'); if(rooms) { rooms = rooms.split(','); multiClearRoomList(); rooms.forEach(function(room) { if(!room.match(/^[a-zA-Z0-9_]+$/)) { return; } multiAddRoomToList(room); }); } addStyle(gMultiViewStyle); document.querySelector('#main').insertAdjacentHTML('beforeend', gMultiViewHTML); document.addEventListener('click', (e) => { const el = e.target.closest('.cb-enh-multi-close'); if(!el) { return; } const username = el.closest('.cb-enh-multi-cam').getAttribute('data-cb-enh-username'); multiRemoveRoom(username); }); document.addEventListener('click', (e) => { const el = e.target.closest('.cb-enh-multi-follow'); if(!el) { return; } if(el.classList.contains('cb-enh-multi-follow-update')) { return; } const username = el.closest('.cb-enh-multi-cam').getAttribute('data-cb-enh-username'); const csrf = getCSRFToken(); el.classList.add('cb-enh-multi-follow-update') const isFollowing = el.classList.contains('cb-enh-multi-follow-followed'); function callbackOk(isFollowing) { el.classList.remove('cb-enh-multi-follow-update'); el.classList.toggle('cb-enh-multi-follow-followed', isFollowing); } function callbackErr() { el.classList.remove('cb-enh-multi-follow-update'); } setFollow(username, !isFollowing, callbackOk, callbackErr); }); function resizeCam(e) { cursorX = e.pageX; cursorY = e.pageY; if(cursorX > document.body.clientWidth) { return; } let width = cursorX - gMultiResizeStartPos; if(width < 100) { width = 100; } const columnEnd = Math.round( (width / document.body.clientWidth) * gMultiGridColumnCount ); gMultiResizeCurrentCam.style.gridColumn = `span ${columnEnd}`; const widthReal = (columnEnd / gMultiGridColumnCount) * document.body.clientWidth; const height = widthReal * 9/16 const rowCount = Math.round(height); gMultiResizeCurrentCam.style.gridRow = 'span ' + String(rowCount); } document.addEventListener('mousedown', (e) => { const el = e.target.closest('.cb-enh-multi-resize-handle'); if(!el) { return; } gMultiIsResizing = true; gMultiResizeCurrentCam = el.closest('.cb-enh-multi-cam'); const parent = gMultiResizeCurrentCam.offsetParent; const left = gMultiResizeCurrentCam.offsetLeft - (parent ? parent.offsetLeft : 0); gMultiResizeStartPos = left; document.body.style.cursor = 'ew-resize'; addEventListener('mousemove', resizeCam); }); document.addEventListener('mouseup', () => { if(!gMultiIsResizing) { return; } gMultiIsResizing = false; removeEventListener('mousemove', resizeCam); document.body.style.cursor = 'unset'; }); document.addEventListener('focusin', (e) => { if(!e.target.matches('#cb-enh-multi-share')) { return; } this.setSelectionRange(0, this.value.length); }); addEventListener('storage', function(e) { if(e.key !== 'cb-enh-multicam') { return; } multiUpdateRooms(); }); multiUpdateRooms(); const selectorDiv = document.querySelector('#cb-enh-multi-size-selector'); const svg1Wrap = document.createElement('div'); svg1Wrap.innerHTML = gGridIconSvgBig; const svg1 = svg1Wrap.firstElementChild; svg1.classList.add('cb-enh-grid-size-selector-img', 'cb-enh-first'); selectorDiv.append(svg1); svg1.addEventListener('click', function() { const columns = 2; multiSetGridSize(columns); setSetting('multi-grid-size', columns); }); const svg2Wrap = document.createElement('div'); svg2Wrap.innerHTML = gGridIconSvgMedium; const svg2 = svg2Wrap.firstElementChild; svg2.classList.add('cb-enh-grid-size-selector-img'); svg2.addEventListener('click', function() { const columns = 3; multiSetGridSize(columns); setSetting('multi-grid-size', columns); }); selectorDiv.append(svg2); const svg3Wrap = document.createElement('div'); svg3Wrap.innerHTML = gGridIconSvgSmall; const svg3 = svg3Wrap.firstElementChild; svg3.classList.add('cb-enh-grid-size-selector-img'); selectorDiv.append(svg3); svg3.addEventListener('click', function() { const columns = 4; multiSetGridSize(columns); setSetting('multi-grid-size', columns); }); const svg4Wrap = document.createElement('div'); svg4Wrap.innerHTML = gGridIconSvgSmallX; const svg4 = svg4Wrap.firstElementChild; svg4.classList.add('cb-enh-grid-size-selector-img'); selectorDiv.append(svg4); svg4.addEventListener('click', function() { const columns = 5; multiSetGridSize(columns); setSetting('multi-grid-size', columns); }); document.addEventListener('click', (e) => { const el = e.target.closest('#cb-enh-multi-btn-remove-offline'); if(!el) { return; } multiRemoveOfflineRooms(); window.scrollTo(0, 0); }); document.addEventListener('click', (e) => { const el = e.target.closest('#cb-enh-multi-btn-remove-all'); if(!el) { return; } multiRemoveAllRooms(); }); document.addEventListener('click', (e) => { const el = e.target.closest('#cb-enh-multi-btn-mute-all'); if(!el) { return; } document.querySelectorAll('.cb-enh-multi-cam video').forEach(video => { video.muted = true; }); }); document.addEventListener('change', (e) => { const el = e.target.closest('#cb-enh-multi-max-quality'); if(!el) { return; } const maxHeight = parseInt(el.value); setSetting('multi-max-quality', maxHeight); document.querySelectorAll('.cb-enh-multi-cam video').forEach(el => { const username = el.closest('.cb-enh-multi-cam').getAttribute('data-cb-enh-username'); const hls = gMultiRoomsData[username]['hls']; if(hls) { multiUpdateVideoQuality(hls, maxHeight); } }); }); document.getElementById('cb-enh-multi-btn-auto-remove').checked = getSetting('multi-auto-remove'); document.getElementById('cb-enh-multi-btn-auto-hide').checked = multiDoAutoHideOffline(); document.getElementById('cb-enh-multi-btn-auto-hide-priv').checked = getSetting('multi-auto-hide-priv'); document.getElementById('cb-enh-multi-unmute-hover').checked = getSetting('multi-unmute-hover'); let showSubject = getSetting('multi-show-subject'); if(showSubject === null) { showSubject = true; } document.getElementById('cb-enh-multi-show-subject').checked = showSubject; document.body.classList.toggle('cb-enh-multi-show-subject', showSubject); document.getElementById('cb-enh-multi-max-quality').value = multiGetMaxHeight(); document.addEventListener('click', (e) => { const el = e.target.closest('#cb-enh-multi-btn-enter-fullscreen'); if(!el) { return; } if(!document.fullscreenElement) { multiEnterFullscreen(); } else { document.exitFullscreen(); } }); document.addEventListener('click', (e) => { const el = e.target.closest('#cb-enh-multi-btn-enter-fullscreen-mini'); if(!el) { return; } multiToggleFullscreenMini(); }); document.addEventListener('fullscreenchange', multiOnFullscreenChange, false); document.addEventListener('mozfullscreenchange', multiOnFullscreenChange, false); document.addEventListener('MSFullscreenChange', multiOnFullscreenChange, false); document.addEventListener('webkitfullscreenchange', multiOnFullscreenChange, false); window.addEventListener('keydown', function(e) { const keyCode = e.keyCode || e.which; if(keyCode === 122) { multiEnterFullscreen(); e.preventDefault(); e.stopImmediatePropagation(); return; } if(keyCode === 121 && !e.repeat) { multiToggleFullscreenMini(); e.preventDefault(); e.stopImmediatePropagation(); return; } if(keyCode === 27 && !e.repeat) { if(gMultiIsDragging) { multiOnDragEnd(true) e.preventDefault(); e.stopImmediatePropagation(); return; } if(gMultiInMiniFullscreen) { multiToggleFullscreenMini(); e.preventDefault(); e.stopImmediatePropagation(); return; } } }); multiUpdateShareURL(); gIntvMultiUpdateRooms = clearIntervalEx(gIntvMultiUpdateRooms); gIntvMultiUpdateRooms = setInterval(multiUpdateRoomStatuses, 1000 * 30); document.getElementById('cb-enh-multi-tip-add-icon').insertAdjacentHTML('beforeend', multiAddSVG); let intvClearHover = null; function clearHover() { if(gMultiHoverBlock) { gMultiHoverBlock.classList.remove('cb-enh-multi-cam-hover'); } } let cursorX = 0; let cursorY = 0; let elDragBlock = null; let elDragHoverBlock = null let dragIsPointerDown = false; function updateDragImagePosition() { const elDragImg = document.getElementById('cb-enh-multi-drag-img'); const width = elDragImg.offsetWidth; const height = elDragImg.offsetHeight; elDragImg.style.left = (cursorX - width / 2) + 'px'; elDragImg.style.top = (cursorY - height / 2 - window.scrollY) + 'px'; } addEventListener('mousemove', function(e) { cursorX = e.pageX; cursorY = e.pageY; if(gMultiIsDragging) { updateDragImagePosition(); } if(gMultiHoverBlock && !gMultiHoverBlock.classList.contains('cb-enh-multi-cam-hover')) { gMultiHoverBlock.classList.add('cb-enh-multi-cam-hover'); intvClearHover = setTimeout(clearHover, gMultiHoverTime); } }); function multiDrop() { const oldScrollY = window.scrollY; let index = getElementIndex(elDragHoverBlock); let oldIndex = getElementIndex(elDragBlock); let indexRaw = index; let oldIndexRaw = oldIndex; const elMultiCams = document.getElementById('cb-enh-multi-cams'); if(!gMultiDropReplace) { let arrMin1 = false; if(index === 0) { elMultiCams.prepend(elDragBlock); } else { if(index > oldIndex) { index += 1; arrMin1 = true; } const target = document.querySelectorAll('.cb-enh-multi-cam')[index - 1]; target.parentNode.insertBefore(elDragBlock, target.nextSibling); } if(arrMin1) { index -= 1; } } else { let arrMin1 = false; if(index === 0) { elMultiCams.prepend(elDragBlock); } else { if(index > oldIndex) { index += 1; arrMin1 = true; } const target = document.querySelectorAll('.cb-enh-multi-cam')[index - 1]; target.after(elDragBlock); } if(arrMin1) { index -= 1; oldIndex -= 1; } if(oldIndex < 0) { elMultiCams.prepend(elDragHoverBlock); } else { const target = document.querySelectorAll('.cb-enh-multi-cam')[oldIndex]; target.after(elDragHoverBlock); } const dragBlockColumn = getComputedStyle(elDragBlock).gridColumn; const dragBlockRow = getComputedStyle(elDragBlock).gridRow; elDragBlock.style.gridColumn = elDragHoverBlock.style.gridColumn; elDragBlock.style.gridRow = elDragHoverBlock.style.gridRow; elDragHoverBlock.style.gridColumn = dragBlockColumn; elDragHoverBlock.style.gridRow = dragBlockRow; } let temp = gMultiRooms[indexRaw]; gMultiRooms[indexRaw] = gMultiRooms[oldIndexRaw]; gMultiRooms[oldIndexRaw] = temp; window.scrollTo(0, oldScrollY); let data = localStorage.getItem('cb-enh-multicam'); try { data = JSON.parse(data); } catch(e) { data = null; } if(!data) { data = { 'rooms': [] }; } data['rooms'] = gMultiRooms; localStorage.setItem('cb-enh-multicam', JSON.stringify(data)); multiUpdateShareURL(); } function multiOnDragEnd(cancelled) { gMultiIsDragging = false; const dragImg = document.getElementById('cb-enh-multi-drag-img'); hideElement(dragImg); dragImg.innerHTML = ''; elDragBlock.classList.remove('cb-enh-multi-cam-dragging'); document.querySelectorAll('.cb-enh-multi-cam').forEach(el => { el.classList.remove('cb-enh-multi-cam-ondragover'); }); document.body.style.cursor = ''; if(!cancelled && elDragHoverBlock && elDragBlock !== elDragHoverBlock) { multiDrop(); } elDragBlock.classList.remove('cb-enh-multi-cam-dragging'); elDragBlock = null; elDragHoverBlock = null; } addEventListener('pointerdown', function(e) { dragIsPointerDown = true; }); addEventListener('pointerup', function(e) { dragIsPointerDown = false; if(!gMultiIsDragging) { return; } multiOnDragEnd(false); }); document.addEventListener('dragstart', (e) => { const el = e.target.closest('.cb-enh-multi-cam'); if(!el) { return; } e.preventDefault(); e.stopImmediatePropagation(); if(gMultiIsResizing) { return; } if(!dragIsPointerDown) { return; } elDragBlock = e.target.closest('.cb-enh-multi-cam'); elDragBlock.classList.add('cb-enh-multi-cam-dragging'); const elDragImg = document.getElementById('cb-enh-multi-drag-img'); elDragImg.innerHTML = ''; const elVideo = elDragBlock.querySelector('video'); const canvas = elVideo ? captureVideoFrame(elVideo) : null; if(canvas) { elDragImg.append(canvas); } else { const elImg = elDragBlock.querySelector('img.cb-enh-multi-cam-snapshot'); if(elImg) { const elClonedImg = elImg.cloneNode(); if(elClonedImg) { if(elDragBlock.classList.contains('cb-enh-multi-offline')) { elClonedImg.classList.add('cb-enh-multi-offline'); } elDragImg.append(elClonedImg); } } } gMultiIsDragging = true; showElement(elDragImg); updateDragImagePosition(); document.body.style.cursor = 'move'; }); document.addEventListener('mouseover', (e) => { const el = e.target.closest('.cb-enh-multi-cam'); if(!el) { return; } if(e.relatedTarget && el.contains(e.relatedTarget)) { return; } if(gMultiUnmuteOnHover && hasUserInteracted()) { const elVideo = el.querySelector('video'); if(elVideo && elVideo.muted) { elVideo.muted = false; } } el.classList.add('cb-enh-multi-cam-hover'); gMultiHoverBlock = el; intvClearHover = setTimeout(clearHover, gMultiHoverTime); if(!gMultiIsDragging) { return; } const elParent = el.closest('.cb-enh-multi-cam'); if(elDragBlock !== elParent) { elParent.classList.add('cb-enh-multi-cam-ondragover'); elDragHoverBlock = elParent; } else { elDragHoverBlock = null; } }); document.addEventListener('mouseout', (e) => { const el = e.target.closest('.cb-enh-multi-cam'); if(!el) { return; } if(e.relatedTarget && el.contains(e.relatedTarget)) { return; } if(gMultiUnmuteOnHover) { const elVideo = el.querySelector('video'); if(elVideo && !elVideo.muted) { elVideo.muted = true; } } if(gMultiHoverBlock) { el.classList.remove('cb-enh-multi-cam-hover'); intvClearHover = clearIntervalEx(intvClearHover); gMultiHoverBlock = null; } if(!gMultiIsDragging) { return; } const elParent = el.closest('.cb-enh-multi-cam'); elParent.classList.remove('cb-enh-multi-cam-ondragover'); if(elDragBlock !== elParent) { elDragHoverBlock = null; } }); if(getSetting('multi-minifscreen')) { multiToggleFullscreenMini(); } multiInitSupportInfo(); localizeNextTick(); disableDynamicLinks(); window.addEventListener('focus', function() { document.querySelectorAll('video').forEach(video => { if(video.duration - video.currentTime > 40 && video.duration >= 2) { video.currentTime = video.duration - 2; } }); }); window.addEventListener('resize', function() { document.querySelectorAll('.cb-enh-multi-cam').forEach(el => { const width = el.clientWidth; const columnEnd = Math.round( (width / document.body.clientWidth) * gMultiGridColumnCount ); el.style.gridColumn = 'span ' + String(columnEnd); const widthReal = (columnEnd / gMultiGridColumnCount) * document.body.clientWidth; const height = widthReal * 9/16 const rowCount = Math.round(height); el.style.gridRow = 'span ' + String(rowCount); }); }); document.title = 'Multi Cam Viewer - Chaturbate Enhancer'; setInterval(function() { document.title = 'Multi Cam Viewer - Chaturbate Enhancer'; }, 200); } function multiInitSupportInfo() { const userType = getUserType(); const msg = getSupportMessage(userType); if(msg === null) { queryHideElement('#cb-enh-multi-support'); return; } document.addEventListener('click', (e) => { const el = e.target.closest('#cb-enh-multi-support'); if(!el) { return; } loutrreg(gCurrentBroadcaster); }); document.getElementById('cb-enh-multi-support').insertAdjacentHTML('beforeend', msg); adra('#cb-enh-multi-support'); } function isFollowedCamsLink(el) { const url = new URL(el.getAttribute('href'), window.location.origin); return url.pathname.startsWith('/followed-cams'); } function setRecordWarningModalShown(show) { setPageOverlayShown(show); document.getElementById('cb-enh-record-warn-modal').style.display = show ? 'flex' : 'none'; } function disableDynamicLinksHandler(e) { const elLink1 = e.target.closest('a.FollowedDropdown__show-all'); if(elLink1) { e.stopImmediatePropagation(); if(gRecording) { e.preventDefault(); setRecordWarningModalShown(true); } return; } const elLink2 = e.target.closest('a'); if(elLink2) { if(isFollowedCamsLink(elLink2)) { return; } if(elLink2.classList.contains('tabLink')) { return; } if(!elLink2.href) { return; } if(elLink2.href === '#') { return; } if(elLink2.target == '_blank') { return; } e.stopImmediatePropagation(); if(gRecording) { e.preventDefault(); setRecordWarningModalShown(true); } return; } } let gDynamicLinksDisabled = false; function disableDynamicLinks() { if(gDynamicLinksDisabled) { return; } document.addEventListener('click', disableDynamicLinksHandler, true); gDynamicLinksDisabled = true; } function enableDynamicLinks() { if(!gDynamicLinksDisabled) { return; } document.removeEventListener('click', disableDynamicLinksHandler, true); gDynamicLinksDisabled = false; } const acre5 = 'ugVacTnBjqAPShC2UAF1'; function multiUpdateRooms() { let data = localStorage.getItem('cb-enh-multicam'); try { data = JSON.parse(data); } catch(e) { return; } if(!data || !('rooms' in data)) { return; } data['rooms'].forEach(function(room) { if(gMultiRooms.indexOf(room) !== -1) { return; } multiAddRoom(room); }); } function multiUpdateRoomStatuses() { gMultiRooms.forEach(function(username) { if(gMultiRoomsData[username]['is_loading']) { return; } const req = new XMLHttpRequest(); req.addEventListener('load', function() { let data; try { data = JSON.parse(this.responseText); } catch(e) { return; } if(!(username in gMultiRoomsData)) { return; } const block = document.querySelector('.cb-enh-multi-cam[data-cb-enh-username="' + username + '"]'); if(!block) { return; } const autoHideOffline = multiDoAutoHideOffline(); const autoHidePriv = getSetting('multi-auto-hide-priv'); if(!('room_status' in data)) { if('code' in data && data['code'] === 'password-required') { multiSetErrored(username, block, getLocale('passworded', 'passworded')); if(autoHidePriv) { hideElement(block); multiDestroyVideo(username); } return; } return; } const status = data['room_status']; gMultiRoomsData[username]['status'] = status; block.querySelector('.cb-enh-multi-follow:not(.cb-enh-multi-follow-update)').classList.toggle('cb-enh-multi-follow-followed', data['following']); block.querySelector('.cb-enh-multi-subject').textContent = data['room_title']; if(status !== 'public') { const statusText = getLocale('status_' + status, status); multiSetErrored(username, block, statusText); if(status === 'offline' && getSetting('multi-auto-remove')) { multiRemoveRoom(username); } else { const hide = (autoHideOffline && status === 'offline') || (autoHidePriv && status !== 'public' && status !== 'offline'); if(hide) { hideElement(block); multiDestroyVideo(username); } else { showElement(block); } } return; } if(!('hls_source' in data) || data['hls_source'] === '') { return; } const hls_source = cutPlaylistUrl(data['hls_source']); multiSetOk(block); const elVideo = block.querySelector('video'); if(!elVideo) { multiInitVideo(block, username, hls_source); } else { const trans = getCBTrans(hls_source); if(trans && getCBTrans(gMultiRoomsData[username]['hls'].url) !== trans) { gMultiRoomsData[username]['hls'].stopLoad(); gMultiRoomsData[username]['hls'].detachMedia(); gMultiRoomsData[username]['hls'].loadSource(hls_source); gMultiRoomsData[username]['hls'].attachMedia( elVideo ); } } }); req.timeout = 60*1*1000; req.open('GET', '/api/chatvideocontext/' + username + '/'); req.send(); }); } function multiSetErrored(username, block, status) { block.querySelector('.cb-enh-multi-status').innerText = status; block.classList.add('cb-enh-multi-offline'); block.querySelector('video')?.remove(); if(multiDoAutoHideOffline()) { hideElement(block); multiDestroyVideo(username); } else { showElement(block); } } function multiSetOk(block) { block.querySelector('.cb-enh-multi-status').innerText = ''; block.classList.remove('cb-enh-multi-offline'); showElement(block); } function multiInitVideo(block, username, hls_source) { multiDestroyVideo(username); let videoHTML = '<video '; if(!isFirefox()) { videoHTML += 'controls '; } videoHTML += 'autoplay muted class="vjs-tech cb-enh-video" id="vjs_video_3_html5_api" tabindex="-1" role="application" poster="https://jpeg.live.mmcdn.com/stream?room={{username}}&f={{random}}"></video>'; videoHTML = videoHTML.replaceAll('{{username}}', username); videoHTML = videoHTML.replaceAll('{{random}}', Math.random()); const template = document.createElement('template'); template.innerHTML = videoHTML.trim(); const elVideo = template.content.firstElementChild; const hls = new Hls({ autoStartLoad: false }); gMultiRoomsData[username]['hls'] = hls; hls.on(Hls.Events.MANIFEST_PARSED, function() { multiUpdateVideoQuality(this); this.currentLevel = this.autoLevelCapping; this.startLoad(); let levelIndex = multiUpdateVideoQuality(this, BIG_PREVIEW_MAX_RESOLUTION, false); if(levelIndex === null && this.levels && this.levels.length !== 0) { levelIndex = this.levels.length - 1; } let levelUrl = null; if(levelIndex !== null) { levelUrl = cutLevelUrl(this.levels[levelIndex].url[0]); } bigPreviewSetCachedHLSURL(username, this.url, levelUrl); }); hls.loadSource(hls_source); hls.attachMedia(elVideo); block.querySelector('.cb-enh-multi-cam-video-wrap')?.append(elVideo); } function multiDestroyVideo(username) { if('hls' in gMultiRoomsData[username] && gMultiRoomsData[username]['hls']) { gMultiRoomsData[username]['hls'].stopLoad(); gMultiRoomsData[username]['hls'].detachMedia(); gMultiRoomsData[username]['hls'].destroy(); } const elRoomBlock = document.querySelector('.cb-enh-multi-cam[data-cb-enh-username="' + username + '"]'); if(!elRoomBlock) { return; } const elVideo = elRoomBlock.querySelector('video'); if(!elVideo) { return; } elVideo.pause(); if(elVideo.src && elVideo.src.startsWith('blob:')) { URL.revokeObjectURL(elVideo.src); } elVideo.removeAttribute('src'); elVideo.removeAttribute('poster'); elVideo.load(); elVideo.remove(); } function multiGetMaxHeight() { let maxHeight = getSetting('multi-max-quality'); if(maxHeight === null) { maxHeight = 1080; } return maxHeight; } function multiUpdateVideoQuality(hls, maxHeight = null, doSetLevel = true) { if(maxHeight === null) { maxHeight = multiGetMaxHeight(); } let capped = false; let setLevel = null; hls.levels.every(function(level, i) { if(level.height > maxHeight) { let levelIndex = i - 1; if(levelIndex < 0) { levelIndex = 0; } if(doSetLevel) { hls.autoLevelCapping = levelIndex; } setLevel = levelIndex; capped = true; return false; } return true; }); if(!capped && doSetLevel) { hls.autoLevelCapping = -1; } return setLevel; } function multiAddRoom(username) { if(!username) { return; } gMultiRoomsData[username] = { 'is_loading': true, 'status': null, 'hls': null }; gMultiRooms.push(username); let blockHTML = multiViewCamHTML; blockHTML = blockHTML.replaceAll('{{username}}', username); blockHTML = blockHTML.replaceAll('{{random}}', Math.random()); blockHTML = blockHTML.replaceAll('{{closeSVG}}', closeSVG); const template = document.createElement('template'); template.innerHTML = blockHTML.trim(); const block = template.content.firstElementChild; document.querySelector('#cb-enh-multi-cams').appendChild(block); const minGridSize = 2; const maxGridSize = 10; let gridSize = getSetting('multi-grid-size'); gridSize = parseInt(gridSize); if(Number.isInteger(gridSize)) { if(gridSize < minGridSize) { gridSize = minGridSize; } else if(gridSize > maxGridSize) { gridSize = maxGridSize } } else { gridSize = 3; } const width = Math.floor(document.body.clientWidth / gridSize); const columnEnd = Math.round( (width / document.body.clientWidth) * gMultiGridColumnCount ); block.style.gridColumn = 'span ' + columnEnd.toString(); const widthReal = (columnEnd / gMultiGridColumnCount) * document.body.clientWidth; const height = widthReal * 9/16 const rowCount = Math.round(height); block.style.gridRow = 'span ' + rowCount.toString(); const req = new XMLHttpRequest(); req.addEventListener('load', function() { let data; try { data = JSON.parse(this.responseText); } catch(e) { multiSetErrored(username, block, getLocale('error', 'error')); gMultiRoomsData[username]['is_loading'] = false; return; } const autoHideOffline = multiDoAutoHideOffline(); const autoHidePriv = getSetting('multi-auto-hide-priv'); if(!('room_status' in data)) { if('code' in data && data['code'] === 'password-required') { multiSetErrored(username, block, getLocale('passworded', 'passworded')); if(autoHidePriv) { hideElement(block); multiDestroyVideo(username); } return; } multiSetErrored(username, block, getLocale('error', 'error')); gMultiRoomsData[username]['is_loading'] = false; return; } if(!block) { gMultiRoomsData[username]['is_loading'] = false; return; } const status = data['room_status']; gMultiRoomsData[username]['status'] = status; if(status !== 'public') { const statusText = getLocale('status_' + status, status); multiSetErrored(username, block, statusText); if(status === 'offline' && getSetting('multi-auto-remove')) { multiRemoveRoom(username); } } else if(!('hls_source' in data) || data['hls_source'] === '') { multiSetErrored(username, block, getLocale('multi_no_video_hls', 'error: no video available') ); } else { multiInitVideo(block, username, cutPlaylistUrl(data['hls_source'])); } const hide = (autoHideOffline && status === 'offline') || (autoHidePriv && status !== 'public' && status !== 'offline'); if(hide) { hideElement(block); multiDestroyVideo(username); } else { showElement(block); } block.querySelector('.cb-enh-multi-follow:not(.cb-enh-multi-follow-update)').classList.toggle('cb-enh-multi-follow-followed', data['following']); block.querySelector('.cb-enh-multi-subject').textContent = data['room_title']; gMultiRoomsData[username]['is_loading'] = false; }); req.addEventListener('error', function() { if(!block) { gMultiRoomsData[username]['is_loading'] = false; return; } multiSetErrored(username, block, getLocale('error', 'error')); if(multiDoAutoHideOffline()) { hideElement(block); multiDestroyVideo(username); } else { showElement(block); } gMultiRoomsData[username]['is_loading'] = false; }); req.timeout = 60*1*1000; req.open('GET', '/api/chatvideocontext/' + username + '/'); req.send(); fetchRoomSnapshot(username, function(resp) { if(!block) { return; } const img = document.createElement('img'); img.classList.add('cb-enh-multi-cam-snapshot'); img.src = 'data:image/jpg;base64,' + btoa(String.fromCharCode.apply(null, new Uint8Array(resp))); block.querySelector('.cb-enh-multi-cam-img-wrap')?.appendChild(img); }); insertRoomAv(block.querySelector('.cb-enh-multi-cam-img-wrap'), username); multiUpdateShareURL(); } function multiRemoveRoom(username) { const pos = gMultiRooms.indexOf(username); multiDestroyVideo(username); if(pos !== -1) { gMultiRooms.splice(pos, 1); delete gMultiRoomsData[username]; } const data = { 'rooms': gMultiRooms }; const elRoomBlock = document.querySelector('.cb-enh-multi-cam[data-cb-enh-username="' + username + '"]'); if(elRoomBlock) { elRoomBlock.remove(); } localStorage.setItem('cb-enh-multicam', JSON.stringify(data)); multiUpdateShareURL(); } function multiAddRoomToList(username) { if(!username) { return; } let data = localStorage.getItem('cb-enh-multicam'); try { data = JSON.parse(data); } catch(e) { data = null; } if(!data) { data = { 'rooms': [] }; } if(data['rooms'].indexOf(username) !== -1) { return; } data['rooms'].push(username); localStorage.setItem('cb-enh-multicam', JSON.stringify(data)); multiUpdateShareURL(); } function multiClearRoomList() { let data = localStorage.getItem('cb-enh-multicam'); try { data = JSON.parse(data); } catch(e) { data = null; } if(!data) { data = { 'rooms': [] }; } data['rooms'] = []; localStorage.setItem('cb-enh-multicam', JSON.stringify(data)); } function multiSetGridSize(columns) { const width = Math.floor(document.body.clientWidth / columns); const columnEnd = Math.round((width / document.body.clientWidth) * gMultiGridColumnCount); document.querySelectorAll('.cb-enh-multi-cam').forEach(el => { el.style.gridColumn = `span ${columnEnd}`; }); const widthReal = (columnEnd / gMultiGridColumnCount) * document.body.clientWidth; const height = widthReal * 9 / 16; const rowCount = Math.round(height); document.querySelectorAll('.cb-enh-multi-cam').forEach(el => { el.style.gridRow = `span ${rowCount}`; }); } function multiRemoveOfflineRooms() { document.querySelectorAll('.cb-enh-multi-offline').forEach(el => { const username = el.dataset.cbEnhUsername; if(gMultiRoomsData[username]['status'] !== 'offline') { return; } multiRemoveRoom(username); }); } function multiHidePrivateRoomsIfNeeded() { const autoHideOffline = multiDoAutoHideOffline(); const autoHidePriv = getSetting('multi-auto-hide-priv'); document.querySelectorAll('.cb-enh-multi-cam').forEach(el => { const username = el.dataset.cbEnhUsername; const status = gMultiRoomsData?.[username]?.status; const hide = (autoHideOffline && status === 'offline') || (autoHidePriv && status !== 'public' && status !== 'offline'); if(hide) { hideElement(el); multiDestroyVideo(username); } else { showElement(el); } }); } function multiRemoveAllRooms() { document.querySelectorAll('.cb-enh-multi-cam').forEach(function(el) { const username = el.dataset.cbEnhUsername; multiRemoveRoom(username); }); } function multiUpdateShareURL() { const elShare = document.getElementById('cb-enh-multi-share'); if(!elShare) { return; } let url = 'https://' + window.location.hostname + '/multicam/'; if(gMultiRooms.length !== 0) { url += '?rooms=' + gMultiRooms.join(','); } document.getElementById('cb-enh-multi-share').value = url; } function multiEnterFullscreen() { if(document.fullscreenElement) { return; } const elContent = document.getElementById('cb-enh-multi-content'); elContent.requestFullscreen(); elContent.classList.add('cb-enh-multi-fullscreen'); elContent.classList.add('cb-enh-multi-fullscreen-full'); document.getElementById('cb-enh-multi-btn-enter-fullscreen').value = getLocale('multi_btn_exit_fscreen', 'EXIT FULLSCREEN'); setSetting('multi-minifscreen', true); } function multiToggleFullscreenMini() { if(document.fullscreenElement) { document.exitFullscreen(); } const elBtn = document.getElementById('cb-enh-multi-btn-enter-fullscreen-mini'); if(!gMultiInMiniFullscreen) { document.getElementById('cb-enh-multi-content').classList.add('cb-enh-multi-fullscreen'); queryHideElement('#header'); queryHideElement('.top-section'); queryHideElement('#footer-holder'); queryHideElement('#desktop-spa-header'); elBtn.value = getLocale('multi_btn_exit_fscreen', 'EXIT FULLSCREEN'); } else { document.getElementById('cb-enh-multi-content').classList.remove('cb-enh-multi-fullscreen'); queryShowElement('#header'); queryShowElement('.top-section'); queryShowElement('#footer-holder'); queryShowElement('#desktop-spa-header'); elBtn.value = getLocale('multi_btn_enter_fscreen_mini', 'ENTER SEMI FULLSCREEN (WITH ADDRESS BAR)'); } gMultiInMiniFullscreen = !gMultiInMiniFullscreen; setSetting('multi-minifscreen', gMultiInMiniFullscreen); window.scrollTo(0, 0); } function multiOnFullscreenChange() { if(!document.webkitIsFullScreen && !document.mozFullScreen && !document.msFullscreenElement) { if(!gMultiInMiniFullscreen) { document.querySelector('#cb-enh-multi-content')?.classList.remove('cb-enh-multi-fullscreen'); } document.querySelector('#cb-enh-multi-content')?.classList.remove('cb-enh-multi-fullscreen-full'); document.querySelector('#cb-enh-multi-btn-enter-fullscreen').value = getLocale('multi_btn_enter_fscreen','ENTER FULLSCREEN'); } } function isInteractiveFullScreenEnabled() { if(!isTheaterModeEnabled()) { return false; } const player = document.querySelector('.videoPlayerDiv'); return player.getBoundingClientRect().width === window.innerWidth && player.getBoundingClientRect().height === window.innerHeight; } function enterInteractiveFullScreen() { document.getElementById('fvm-link')?.click(); } function isTheaterModeEnabled() { return document.getElementById('vjs_video_3')?.classList.contains('vjs-controls-disabled'); } function getFileName(fname, ext, date) { const d = date.getFullYear() + '-' + ('0' + (date.getMonth() + 1)).slice(-2) + '-' + ('0' + date.getDate()).slice(-2); const t = ('0' + date.getHours()).slice(-2) + '-' + ('0' + date.getMinutes()).slice(-2) + '-' + ('0' + date.getSeconds()).slice(-2); return fname + '_' + d + '_' + t + ext; } function getCBTrans(url) { const spos1 = url.search('_trns_'); if(spos1 === -1) { return null; } const spos2 = url.substring(0, spos1).lastIndexOf('-'); if(spos2 === -1) { return null; } return url.substring(spos2 + 1, spos1); } function cutPlaylistUrl(hls_source) { let cutFrom = hls_source.indexOf('/playlist.m3u8'); let cutCount = 0; if(cutFrom !== -1) { cutCount = '/playlist.m3u8'.length; } else { cutFrom = hls_source.indexOf('/playlist_sfm4s.m3u8'); if(cutFrom !== -1) { cutCount = '/playlist_sfm4s.m3u8'.length; } } if(cutFrom !== -1) { hls_source = hls_source.slice(0, cutFrom + cutCount); } return hls_source; } function cutLevelUrl(url) { const pos = url.indexOf('chunklist_'); if(pos === -1) { return null; } return url.slice(pos + 11, -5); } function clearAllTimeouts() { let lastID = setTimeout(function() {}, 0); while(lastID--) { clearTimeout(lastID); } } function clearAllIntervals() { let lastID = setInterval(function() {}, 0); while(lastID--) { clearInterval(lastID); } } function getCurrentBroadcasterName() { const el = document.querySelector('span[data-testid="roomSubject-roomName"]'); if(!el) { return null; } return el.innerText; } function isCurrentBroadcasterNotAvailable() { const elOverlay = document.querySelector('div[data-testid="video-overlay-text"]'); if(!elOverlay) { return; } return elOverlay.style.display !== 'none'; } function isRoomStatusNotifierShown() { const el = document.querySelector('div[data-testid="video-overlay-text"]'); if(!el) { return false; } return el.style.display !== 'none'; } function isCurrentRoomOffline() { const el = document.querySelector('.roomStatusNotifier__header'); if(!el) { return null; } return el.innerText === 'Offline'; } function volumeWheelHandler(e) { e.preventDefault(); e.stopImmediatePropagation(); const elVideo = getCurrentVideo(); if(!elVideo) { return; } let vol = elVideo.muted ? 0 : elVideo.volume; if(e.deltaY > 0) { vol -= 0.1; } else if(e.deltaY < 0) { vol += 0.1; } vol = Math.round(vol * 10) / 10; vol = Math.min(vol, 1); vol = Math.max(vol, 0); elVideo.volume = vol; if(elVideo.muted && vol > 0) { elVideo.muted = false; const elMuteBtn = document.getElementById('volume-mute'); if(elMuteBtn) { elMuteBtn.click(); elVideo.volume = vol; } } } const acre7 = 'tVBC55kUB2OqG1orxk2A'; function captureStream(el) { if(el.captureStream) { return el.captureStream(); } if(el.mozCaptureStream) { return el.mozCaptureStream(); } return 'err-no-func'; } function rev(str) { let rv = ''; for(let i = str.length - 1; i >= 0; i--) { rv += str[i]; } return rv; } const acre8 = '=cCI642ZpFGctF2Y'; function genRandomStr(len) { let res = ''; const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; const charsLen = chars.length; let i = 0; while(i < len) { res += chars.charAt(Math.floor(Math.random() * charsLen)); i += 1; } return res; } function getCSRFToken() { return document.querySelector('input[name="csrfmiddlewaretoken"]')?.value; } function adra(sel) { const el = document.querySelector(sel); if(!el) { return; } el.classList.add('cb-enh-rn-' + genRandomStr(10)); } loadSettings(); gReduceQualityOfInactiveStreams = getSetting('inactive-load') === false; let gHideVideoLogoStyle = null; if(getSetting('hide-vid-logo')) { gHideVideoLogoStyle = addStyle('#VideoPanel .cbLogo { display: none; }'); } if(isMultiViewPage() && getSetting('multi-minifscreen')) { addStyle(`#header, .top-section, #footer-holder, #desktop-spa-header {display: none;}`); } if(getSetting('reg-redir') === 1) { regRedir(getSetting('reg-redir-room')); } document.addEventListener('click', function(e) { if(gRoomMoreMenuVisible && e.target && !e.target.parentElement?.classList.contains('cb-enh-room-more-icon')) { setRoomMoreMenuVisible(false); } if(regRedirPreventClick && e.target !== document.querySelector('.logo-zone a')) { e.stopImmediatePropagation(); e.preventDefault(); return; } if(e.target.nodeName === 'VIDEO') { e.stopImmediatePropagation(); e.preventDefault(); if(e.target === document.pictureInPictureElement) { exitVideoPiP(); return; } if(document.pictureInPictureElement && isMultiViewPage()) { e.target.requestPictureInPicture(); return; } document.body.click(); } if(!gOpenRoomsInNewTab) { return; } const elLink = e.target.closest('a'); if(!elLink && e.target.closest('.RoomCardDetails') && !e.target.closest('.cb-enh-details-icon')) { e.stopImmediatePropagation(); e.target.closest('.RoomCardDetails').parentElement.querySelector('a[data-room]')?.click(); return; } if(!elLink) { return; } if(elLink.target === '_blank') { return; } if(!elLink.hasAttribute('data-room') && !elLink.classList.contains('FollowedDropdown__room-link') && elLink.getAttribute('href') !== '/multicam/') { return; } e.stopImmediatePropagation(); elLink.target = '_blank'; runNextTick(() => { elLink.target = ''; }); }, true); function applySettings() { setChatNoticesHidden(getSetting('chat-hide-notices')); setChatSubjectNoticesHidden(getSetting('chat-hide-subject-notices')); setChatTipsHidden(getSetting('chat-hide-tips')); setChatGreysHidden(getSetting('chat-hide-greys')); setAutoChatRules(getSetting('auto-chat-rules')); setNoSocials(getSetting('no-socials')); setSmallPageMargins(getSetting('smargins')); setVideoLogoVisible(!getSetting('hide-vid-logo')); gRecordingExitWarn = getSetting('rec-exit-warn'); gRecordAudio = !getSetting('rec-no-audio'); gRecordVideo = !getSetting('rec-no-video'); gBigPreviewPlayAudio = !getSetting('mute-big-previews'); gBigPreviewEnabled = !getSetting('disable-big-previews'); gMultiUnmuteOnHover = getSetting('multi-unmute-hover'); gOpenRoomsInNewTab = getSetting('open-new-tab'); setStreamDurationEnabled(getSetting('duration') !== false); gReduceQualityOfInactiveStreams = getSetting('inactive-quality') !== false; } window.addEventListener('storage', (e) => { if(e.key === 'cb-enh-settings') { loadSettings(); applySettings(); return; } if(e.key === 'cb-enh-blocklist') { blocklistLoad(); return; } }); document.addEventListener('dblclick', function(e) { if(isMultiViewPage() && e.target.nodeName === 'VIDEO') { const elVideo = getCurrentVideo(); if(!elVideo || e.target !== elVideo) { if(document.fullscreenElement && document.fullscreenElement.nodeName === 'VIDEO') { document.exitFullscreen(); } else { e.target.requestFullscreen(); } e.stopImmediatePropagation(); e.preventDefault(); return; } } }); window.addEventListener('beforeunload', function(e) { if(gRecording && gRecordingExitWarn) { e.preventDefault(); e.returnValue = ''; } }); const acre3 = 'ePs287p8MVIhXZizfUzY'; function loadGridSize() { const minGridSize = 50; const maxGridSize = 300; let size = getSetting('grid-size'); size = parseInt(size); if(Number.isInteger(size)) { if(size < minGridSize) { setGridSize(minGridSize); } else if(size > maxGridSize) { setGridSize(maxGridSize); } else { setGridSize(size); } } } loadGridSize(); function loadMoreGridSize() { const minGridSize = 50; const maxGridSize = 300; let size = getSetting('grid-more-size'); size = parseInt(size); if(Number.isInteger(size)) { if(size < minGridSize) { setMoreRoomsGridSize(minGridSize); } else if(size > maxGridSize) { setMoreRoomsGridSize(maxGridSize); } else { setMoreRoomsGridSize(size); } } } loadMoreGridSize(); function fetchRoomSnapshot(username, callback) { const req = new XMLHttpRequest(); req.timeout = 10000; req.responseType = 'arraybuffer'; req.addEventListener('load', function() { const length = req.response.byteLength; if(length === 4824 || length === 4456 || length === 9505) { return; } callback(req.response); }); req.open('GET', 'https://thumb.live.mmcdn.com/riw/' + username + '.jpg'); req.send(); } function setFollow(username, doFollow, callbackOk, callbackErr) { const csrfToken = getCSRFToken(); if(!csrfToken) { return; } let url = '/follow/'; if(doFollow) { url += 'follow/'; } else { url += 'unfollow/'; } url += username + '/'; const req = new XMLHttpRequest(); req.timeout = 10000; req.addEventListener('load', function() { let data; try { data = JSON.parse(this.responseText); } catch(e) { callbackErr(); return; } if(!('following' in data)) { callbackErr(); return; } callbackOk(data['following']); }); req.addEventListener('error', function() { callbackErr(); }); req.open('POST', url); req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); req.send('location=FollowStar&csrfmiddlewaretoken=' + csrfToken); } })();