// ==UserScript== // @name Tiktok Video Controls // @version 3.0 // @author VoidCrab // @description Web player controls and volume reduction to 15% // @include *tiktok.com/* // @grant GM_addStyle // @namespace https://greasyfork.org/users/469070 // @downloadURL https://update.greasyfork.icu/scripts/400793/Tiktok%20Video%20Controls.user.js // @updateURL https://update.greasyfork.icu/scripts/400793/Tiktok%20Video%20Controls.meta.js // ==/UserScript== // set the desired player volume here // to disable this feature, set it to -1 const playerVolume = 0.15; // Taken from https://gist.github.com/BrockA/2625891 and altered to not use jquery /*--- waitForKeyElements(): A utility function, for Greasemonkey scripts, that detects and handles AJAXed content. Usage example: waitForKeyElements ( "div.comments" , commentCallbackFunction ); //--- Page-specific function to do what we want when the node is found. function commentCallbackFunction (jNode) { jNode.text ("This comment changed by waitForKeyElements()."); } IMPORTANT: This function requires your script to have loaded jQuery. */ function waitForKeyElements ( selectorTxt, /* Required: The jQuery selector string that specifies the desired element(s). */ actionFunction, /* Required: The code to run when elements are found. It is passed a jNode to the matched element. */ bWaitOnce, /* Optional: If false, will continue to scan for new elements even after the first match is found. */ iframeSelector /* Optional: If set, identifies the iframe to search. */ ) { var targetNodes, btargetsFound; if (typeof iframeSelector == "undefined") { targetNodes = document.querySelectorAll(selectorTxt); } else { targetNodes = document.querySelectorAll(iframeSelector).contents() .find(selectorTxt); } if (targetNodes && targetNodes.length > 0) { btargetsFound = true; /*--- Found target node(s). Go through each and act if they are new. */ targetNodes.forEach ( (tNode) => { console.log("node", tNode); var alreadyFound = tNode.alreadyFound || false; if (!alreadyFound) { //--- Call the payload function. var cancelFound = actionFunction (tNode); if (cancelFound) { btargetsFound = false; } else { tNode.alreadyFound = true; } } } ); } else { btargetsFound = false; } //--- Get the timer-control variable for this selector. var controlObj = waitForKeyElements.controlObj || {}; var controlKey = selectorTxt.replace (/[^\w]/g, "_"); var timeControl = controlObj [controlKey]; //--- Now set or clear the timer as appropriate. if (btargetsFound && bWaitOnce && timeControl) { //--- The only condition where we need to clear the timer. clearInterval (timeControl); delete controlObj [controlKey] } else { //--- Set a timer, if needed. if ( ! timeControl) { timeControl = setInterval ( function () { waitForKeyElements ( selectorTxt, actionFunction, bWaitOnce, iframeSelector ); }, 300 ); controlObj [controlKey] = timeControl; } } waitForKeyElements.controlObj = controlObj; } // wait until page loaded waitForKeyElements ("video", addControlsToVideo); function addControlsToVideo (player) { // var player = jNode.querySelector("video"); player.setAttribute("controls", ""); player.setAttribute("z-index", 1000); player.style.zIndex = 1000; if (playerVolume != -1) { player.volume = playerVolume; } } /* part of the code to detect big video player that appears once you click a tiktok example page: https://www.tiktok.com/music/Go-Bananas-6758968635772897281?language=en&lang=en after altering code, not longer necessary */ const observeBigPlayer = (feed) => { //const feed = document.querySelector(".tt-feed"); if (!feed) { return; } console.log(feed) new MutationObserver(() => { let big_player = feed.querySelector(".video-card-big.browse-mode").querySelector("video"); actionFunction(big_player); }).observe(feed, { childList: true }); } // after altering code, no longer necessary // waitForKeyElements(".tt-feed", observeBigPlayer)