Fork of YouTube Automatic BS Skip 2.9.1 by JustDaile https://greasyfork.org/en/scripts/392459-youtube-automatic-bs-skip . Automatic skipping of fixed length intros/outros for your favourite Youtube channels.
// ==UserScript== // @name s Youtube Automatic BS Skip 2.9.1 fork May2023 bugfix // @namespace https://greasyfork.org/en/scripts/392459-youtube-automatic-bs-skip // @version 2.9.1.13 // @description Fork of YouTube Automatic BS Skip 2.9.1 by JustDaile https://greasyfork.org/en/scripts/392459-youtube-automatic-bs-skip . Automatic skipping of fixed length intros/outros for your favourite Youtube channels. // @license MIT // @match https://www.youtube.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @require http://code.jquery.com/jquery-latest.js // @run-at document-start // ==/UserScript== console.log(`${GM.info.script.name} run`) //https://greasyfork.org/scripts/392459-youtube-automatic-bs-skip/code/Youtube%20Automatic%20BS%20Skip.user.js const app = "YouTube Automatic BS Skip"; const version = '2.9.1_DaileAlimoMIT_edited'; const debug = false; const log = function(line){if (debug) console.log(line)} // Elements const controlUI_ID = "outro-controls"; const modal_ID = "modal"; const progressBar_ID = "progress-bar"; const introLen_ID = "intro-length"; const outroLen_ID = "outro-length"; const channelTxt_ID = "channel_txt"; // Actions const pauseOnOutro = "pause-on-outro"; const nextOnOutro = "next-on-outro"; const apply_ID = "apply"; // add indicators to the progress bar. const setupProgressBar = function(selector) { log('called setupProgressBar'); if($(`#${progressBar_ID}-intro`).remove()){log("removed intro bar");} if($(`#${progressBar_ID}-outro`).remove()){log("removed outro bar");} // add intro indicator to progress bar if (!document.getElementById(`${progressBar_ID}-intro`)){ log('created intro indicator'); selector.prepend( $(`<div id="${progressBar_ID}-intro">`).addClass("ytp-load-progress").css({ "left": "0%", "transform": "scaleX(0)", }) ); } // add outro indicator to progress bar if (!document.getElementById(`${progressBar_ID}-outro`)) { log('created outro indicator'); selector.prepend( $(`<div id="${progressBar_ID}-outro">`).addClass("ytp-load-progress").css({ "left": "100%", "transform": "scaleX(0)", }) ); } return [`${progressBar_ID}-intro`, `${progressBar_ID}-outro`]; }; // update the indecators on the progressbar. const updateProgressbars = function(intro, outro, duration) { // update the intro progress bar let introBar = $(`#${progressBar_ID}-intro`); var introFraction = intro / duration; introBar.css({ "left": "0%", "transform": `scaleX(${introFraction})`, "background-color": "green", }); // update the outro progress bar let outroBar = $(`#${progressBar_ID}-outro`); var outroFraction = outro / duration; outroBar.css({ "left": `${100 - (outroFraction * 100)}%`, "transform": `scaleX(${outroFraction})`, "background-color": "green", }); }; const setupControls = function(selector) { // Its easier to modify if we don't chain jquery.append($()) to build the html components if($(`#${controlUI_ID}`).remove()){log("removed controls");} var controls = document.getElementById(controlUI_ID); if (controls == null) { log('adding controls to video'); controls = selector.prepend(` <button id="${controlUI_ID}" class="ytp-button loading" title="YABSS" aria-label="YABSS"> <div class="ytp-autonav-toggle-button-container"> <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path fill="white" d="M19 9l1.25-2.75L23 5l-2.75-1.25L19 1l-1.25 2.75L15 5l2.75 1.25L19 9zm-7.5.5L9 4 6.5 9.5 1 12l5.5 2.5L9 20l2.5-5.5L17 12l-5.5-2.5zM19 15l-1.25 2.75L15 19l2.75 1.25L19 23l1.25-2.75L23 19l-2.75-1.25L19 15z"/></svg> </div> </button> `); } if($(`#${modal_ID}`).remove()){log("removed modal");} if (document.getElementById(modal_ID) == null) { log('adding modal to DOM'); $('body').append(` <div id="${modal_ID}"> <div id="${modal_ID}-escape"></div> <div id="${modal_ID}-content"> <div id="${channelTxt_ID}">Loading Channel</div> <h2 id="${controlUI_ID}-title" class="d-flex justify-space-between">YouTube Automatic BS Skip ${version} <a href="https://www.buymeacoffee.com/JustDai" target="_blank"> <svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24" viewBox="0 0 24 24" width="24"> <g><path d="M0,0h24v24H0V0z" fill="none"></path></g> <g fill="#ffffff"><path d="M18.5,3H6C4.9,3,4,3.9,4,5v5.71c0,3.83,2.95,7.18,6.78,7.29c3.96,0.12,7.22-3.06,7.22-7v-1h0.5c1.93,0,3.5-1.57,3.5-3.5 S20.43,3,18.5,3z M16,5v3H6V5H16z M18.5,8H18V5h0.5C19.33,5,20,5.67,20,6.5S19.33,8,18.5,8z M4,19h16v2H4V19z"></path></g> </svg> </a> </h2> <div id="${controlUI_ID}-control-wrapper"> <div class="w-100 d-flex justify-space-around align-center"> <label for="${introLen_ID}">Intro</label> <input type="number" min="0" id="${introLen_ID}" placeholder="unset" class="input"> </div> <div class="w-100 d-flex justify-space-around align-center"> <label for="${outroLen_ID}">Outro</label> <input type="number" min="0" id="${outroLen_ID}" placeholder="unset" class="input"> </div> <div class="pa"> <div> <label for="${controlUI_ID}-outro-action-group">Action on outro <span id='${controlUI_ID}-outro-settings'>(Disabled. Assign outro time to enable)</span></label> </div> <fieldset id="${controlUI_ID}-outro-action-group" class="d-flex"> <div> <label for="${pauseOnOutro}">Pause Video</label> <input type="radio" name="outro-action-group" id="${pauseOnOutro}"> </div> <div> <label for="${nextOnOutro}">Play Next Video</label> <input type="radio" name="outro-action-group" id="${nextOnOutro}" checked="checked"> </div> </fieldset> </div> <tp-yt-paper-button id="${apply_ID}" class="style-scope ytd-video-secondary-info-renderer d-flex justify-center align-center" style-target="host" role="button" elevation="3" aria-disabled="false">${apply_ID}</tp-yt-paper-button> </div> </div> </div>` ); var validate=x=>{ var y=$(`#${x}`).val().toString() if(y && y != "" && parseInt(y) != NaN){ if (y < 0) y = 0 return Number(y) } return } $(`#${controlUI_ID}`).on('click', () => { $(`#${modal_ID}`).toggleClass("show"); }); $(`#${modal_ID}-escape`).on('click', () => { $(`#${modal_ID}`).removeClass("show"); }); $(`#${outroLen_ID}`).on('input', () => { var outroSeconds = validate(outroLen_ID) if(outroSeconds!=undefined) $(`#${controlUI_ID}-outro-settings`)[outroSeconds>0?'addClass':'removeClass']('hide') }) $(`#${apply_ID}`).on("click", function() { var introSeconds = validate(introLen_ID) if(introSeconds!=undefined) updateapp({intro: introSeconds}) var outroSeconds = validate(outroLen_ID) if(outroSeconds!=undefined) updateapp({outro: outroSeconds}) updateapp({outact: $(`#${nextOnOutro}`).get(0).checked?true:false }) $(`#${modal_ID}`).removeClass("show") }); } return controls; }; const updateControls = ({loadedIntroSetInSeconds, loadedOutroSetInSeconds, channelTxt, tooltipTxt, loading, playNextOnOutro}) => { if (channelTxt!=undefined) $(`#${channelTxt_ID}`).text(channelTxt) if (tooltipTxt!=undefined) $(`#${controlUI_ID}`).attr('title', tooltipTxt) if (loading!=undefined) $(`#${controlUI_ID}`)[loading?'addClass':'removeClass']('loading') if (loadedIntroSetInSeconds!=undefined) $(`#${introLen_ID}`).attr("placeholder", loadedIntroSetInSeconds <= 0? "unset": loadedIntroSetInSeconds) if (loadedOutroSetInSeconds!=undefined){ $(`#${outroLen_ID}`).attr("placeholder", loadedOutroSetInSeconds <= 0? "unset": loadedOutroSetInSeconds) $(`#${controlUI_ID}-outro-settings`)[loadedOutroSetInSeconds>0?'addClass':'removeClass']('hide') } if (playNextOnOutro!=undefined){ $(`#${nextOnOutro}`).get(0).checked= playNextOnOutro?"checked":false $(`#${pauseOnOutro}`).get(0).checked= playNextOnOutro?false:"checked" } }; var introid=x=>x.split(" ").join("_") + "-intro" var outroid=x=>x.split(" ").join("_") + "-outro" var outactid=x=>x.split(" ").join("_") + "-outro-action" var curchannel const CHNLOAD='loading' var loadedIntroSetInSeconds var loadedOutroSetInSeconds var playNextOnOutro var updateapp=({channel,intro,outro,outact})=>{ if(channel) curchannel=channel if(curchannel===CHNLOAD){ updateControls({ channelTxt: 'N/A', tooltipTxt: 'YABSS Loading', loading: true, }) }else{ if(intro!=undefined) GM_setValue(introid(curchannel), intro) if(outro!=undefined) GM_setValue(outroid(curchannel), outro) loadedIntroSetInSeconds = Number(GM_getValue(introid(curchannel), 0)) loadedOutroSetInSeconds = Number(GM_getValue(outroid(curchannel), 0)) if(outact!=undefined) GM_setValue(outactid(curchannel),outact) playNextOnOutro = GM_getValue(outactid(curchannel), false) updateControls({ channelTxt: curchannel, tooltipTxt: `${curchannel}\nIntro: ${loadedIntroSetInSeconds}s\nOutro: ${loadedOutroSetInSeconds}s${loadedOutroSetInSeconds>0?playNextOnOutro?' (Play Next)':' (Pause)':''}`, loading: false, loadedIntroSetInSeconds: loadedIntroSetInSeconds, loadedOutroSetInSeconds: loadedOutroSetInSeconds, playNextOnOutro: playNextOnOutro }) } } // var vs var curdur var vsloop=e=>{ var v=e.target if (curdur != v.duration && !isNaN(v.duration) ) { //do these one time per duration change curdur = v.duration updateProgressbars(loadedIntroSetInSeconds, loadedOutroSetInSeconds, v.duration) if(v.currentTime < loadedIntroSetInSeconds) v.currentTime = loadedIntroSetInSeconds } if(loadedOutroSetInSeconds>0 && v.duration-loadedOutroSetInSeconds <= v.currentTime){ v.pause() if(playNextOnOutro) $(".ytp-next-button")[0].click(); } } var vsupdate=_=>{ if(vs!=document.querySelector('.video-stream')){ vs=document.querySelector('.video-stream') vs.addEventListener('timeupdate',vsloop) } setTimeout(vsupdate,1) } vsupdate() // const forkJoinJQExistCheck = function({selectors,func1,func2}) { var $sins=selectors.map(s=>$(s)) if($sins.filter(x=>x.length /*.length jquery existence check */ ).length===selectors.length){ $sins.map((s,i)=>func1[i](s)) func2() } }; // var lasturl var cp=_=>{ if(lasturl!=document.URL){ updateapp({channel:CHNLOAD}) if(document.URL.includes("watch?")){ forkJoinJQExistCheck({ selectors: [ '.video-stream', ".ytp-right-controls", ".ytp-progress-bar"], func1:[ _=>{}, setupControls, setupProgressBar ], func2: async function() { lasturl=document.URL updateapp({channel:CHNLOAD}) var thisurl=document.URL var channel = await fetch(thisurl).then(a=>a.text()).then(a=>a.match(/"author":"(.*?)"/)[1]) if(thisurl===document.URL){ // not switched to another video while fetching channel/author curdur=0 if(true){ // a redundant intro skip for the beginning few seconds to tackle 'timeupdate' latency var v=document.querySelector('.video-stream') var p=!v.paused v.pause() vsloop({target:v}) if(p) v.play() } updateapp({channel:channel}) } } }) } } setTimeout(cp,100) } ;(_=>cp())(); // Write the CSS rules to the DOM GM_addStyle(` #${modal_ID}-escape { position: fixed; left: 0; top: 0; width: 100vw; height: 100vh; z-index: 1000; } #${modal_ID} { display: none; position: fixed; left: 0; top: 0; width: 100vw; height: 100vh; z-index: 999; background: rgba(0,0,0,.8); } #${modal_ID}.show { display: flex; } #${modal_ID}-content { margin: auto; width: 30%; height: auto; background-color: var(--yt-live-chat-action-panel-background-color); border-radius: 6px 6px 6px; border: 1px solid white; padding: 15px; color: white; z-index: 1001; } #${introLen_ID},#${outroLen_ID} { font-size: 1.2em; padding: .4em; border-radius: .5em; border: none; width: 80% } #${apply_ID} { position: relative; border: 1px solid white; transition: background-color .2s ease-in-out } #${apply_ID}:hover { background-color: rgba(255,255,255,0.3); } #${controlUI_ID}.loading { opacity:.3 } #${controlUI_ID} { height: 100%; padding: 0; margin: 0; bottom: 45%; position: relative; } #${controlUI_ID} svg { position: relative; top: 20%; left: 20%; } #${controlUI_ID}-panel { margin-right: 1em; vertical-align:top } #${controlUI_ID} > * { display: inline-block; max-height: 100%; } #${controlUI_ID}-title { padding: 2px; } #${controlUI_ID}-outro-action-group { padding: .5em; } #${controlUI_ID}-outro-action-group > div { display: block; margin: auto; text-align-last: justify; } #${controlUI_ID}-control-wrapper > * { padding: 1em; } #action-radios { display: none; } #action-radios .actions{ padding-left: 2px; text-align: left; background-color: black; color: white; } #${introLen_ID},#${outroLen_ID} { margin-right: 2px; } #${channelTxt_ID} { position: relative; top: -3.5em; margin-bottom: -1.5em; } .w-100 { width: 100% !important; } .input { padding: .2em; } .d-flex { display: flex; } .justify-center { justify-content: center; } .justify-space-around { justify-content: space-around; } .justify-space-between { justify-content: space-between; } .align-center { align-items: center; } #${controlUI_ID}-outro-settings.hide{ display:none } `);