// ==UserScript== // @name stream4chan // @namespace http://tampermonkey.net/ // @version 1.4 // @description Click the button to stream all webms in a 4chan thread // @author Lauchlan105 // @require http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js // @require http://code.jquery.com/jquery-latest.min.js // @match http://boards.4chan.org/*/thread/* // @grant none // @downloadURL none // ==/UserScript== ///////////////////////////////////// //////////////Settings/////////////// ///////////////////////////////////// settingsArray = [ //Start Stream4Chan Automatically true, //Debug on startup false, //Gif duration increment value 0.25, //Gif duration (seconds) 3, //Automatically play next video true, //Play Webms true, //Play Gifs true, //Play thread on repeat true, //Start thread in random order false, //img to use for loading gif 'https://raw.githubusercontent.com/gabrielgio/FChan/master/4chan.png' ]; ///////////////////////////////////// //////////////Settings/////////////// ///////////////////////////////////// //Main (function() { createElements(); initElements(); startEvents(); if(autoStart){ start(); } })(); function createElements(){ //Using Settings Array Variables var z = 0; //Keeps count of array option correspondence to make adding options easier autoStart = settingsArray[z++]; debugOn = settingsArray[z++]; countStep = settingsArray[z++]; var durationDefault = settingsArray[z++]; var autoPlayDefault = settingsArray[z++] ? 'checked' : ''; var playWebmsDefault = settingsArray[z++] ? 'checked' : ''; var playGifsDefault = settingsArray[z++] ? 'checked' : ''; var loopThreadDefault = settingsArray[z++] ? 'checked' : ''; var randomDefault = settingsArray[z++] ? 'checked' : ''; //Loading Spinner spinner = document.createElement("img"); spinner.setAttribute("src",settingsArray[z++]); spinner.setAttribute("id","stream4chan-spinner"); spinner.setAttribute("class","SFC-loading"); //Arrays hiddenPrints = []; hrefs = []; backup = []; preload(hrefs); //Settings Header var modalSettings_auto = ' Play Automatically'; var modalSettings_webms = ' Play Webms'; var modalSettings_gifs = ' Play Gifs'; var modalSettings_loopAll = ' Loop whole thread'; var modalSettings_shuffle = ''; var modalSettings_random = ' Random'; var modalSettings_duration = ' Gif Duration (Seconds)'; var modalSettings_exit = '
'; var modalSettings = '
' + modalSettings_auto + modalSettings_webms + modalSettings_gifs + modalSettings_loopAll + modalSettings_shuffle + modalSettings_random + modalSettings_duration + modalSettings_exit + '
'; //Content Table var modalContent = '
'; var modalContent_Left = ''; var modalContent_Right = ''; var modalContent_Mid = '' + modalSettings + modalContent + ''; var modalTable = '' + modalContent_Left + modalContent_Mid + modalContent_Right + '
'; //Main Div var startBtn = ''; var resumeBtn = ''; var modalMain = '
' + modalTable + '
'; //CSS var settingsCSS = '.SFC-settings { opacity: 0.35; display: inline-block; list-style-type: none; width:100%; top: 0; height: auto; margin: 0em; padding-top: 0.5em; color:#9d9393; }'; var hoverCSS = '.SFC-settings:hover { opacity: 1; } .SFC-settings:last-child:hover { opacity: 0; }'; var settingsInputCSS = '.SFC-input { margin: 0em 0em 0em 1.5em; padding: 0em; }'; var settingsExitCSS = '.SFC-exit { float: right; height: 28px; width: 28px; background-image: url("https://upload.wikimedia.org/wikipedia/commons/thumb/7/72/VisualEditor_-_Icon_-_Close_-_white.svg/2000px-VisualEditor_-_Icon_-_Close_-_white.svg.png"); background-size: contain; }'; var AllSettingsCSS = settingsCSS + hoverCSS + settingsInputCSS + settingsExitCSS; //table var tableCSS = '.SFC_table { max-height: 100vh; min-height: 100vh; max-width: 100vw; min-width: 100vw; } '; var tableRowCSS = '.SFC_table_row { vertical-align: top; }'; var arrowCSS = '.SFC_arrow { width: 15px; padding: 15px; height: 100%; background-image: url("http://www.dsetechnology.co.uk/images/disclose-arrow.png"); background-repeat: no-repeat; background-position: center; background-color: rgba(255, 255, 255, 0); background-size: contain; }'; var leftCSS = '.SFC_th_left { opacity: 0.14; transform: rotate(180deg); }' + '.SFC_th_left:hover { opacity: 1; background-color:rgba(255, 255, 255, 0.6); }'; var rightCSS = '.SFC_th_right { opacity: 0.14; }' + '.SFC_th_right:hover { opacity: 1; background-color:rgba(255, 255, 255, 0.6); }'; var midCSS = '.SFC_th_mid { height: 100vh; }'; var allTableCSS = tableCSS + tableRowCSS + arrowCSS + leftCSS + rightCSS + midCSS; var mediaCSS = '.SFC-media { width: 100%; }'; var contentCSS = '.SFC-content { cursor: default; display: block; }'; var modalCSS = ' .SFC-modal { display: none; height: 100vh; width: 100vw; position: fixed; z-index: 1; left: 0; top: 0; background-color: rgba(0,0,0,0.88); -webkit-box-shadow: inset 0px 0px 71px 41px rgba(0,0,0,0.75); -moz-box-shadow: inset 0px 0px 71px 41px rgba(0,0,0,0.75); box-shadow: inset 0px 0px 71px 41px rgba(0,0,0,0.75);}'; var loadingCSS = '.SFC-loading { margin-top: 100%idth: 200px; height: 200px; animation-name: load; animation-duration: 3s; animation-iteration-count: infinite;animation-timing-function: ease-in-out;}' + '@keyframes load { 0%{ transform: rotate(0deg); } 80%{ transform: rotate(360deg); } 100%{ transform: rotate(360deg); } }'; var allModalCSS = loadingCSS + mediaCSS + contentCSS + modalCSS; var allCSS = ''; //Add start and resume buttons var nav = document.getElementsByClassName('navLinks desktop'); for(var i = 0; i < nav.length; i++){ debug('adding button to nav ' + i); var span = document.createElement('span'); span.innerHTML = startBtn + resumeBtn; span.className = 'stream4chan-start'; span.style.display = nav[i].style.display; nav[i].parentNode.insertBefore(span, nav[i]); nav[i].parentNode.insertBefore(document.getElementById('op'), nav[i]); } //add the modal to start of thread var target = document.getElementsByClassName('thread'); for(i = 0; i < target.length; i++){ target[i].innerHTML = (modalMain + allCSS) + target[i].innerHTML; } } function initElements(){ modal = document.getElementById('stream4chan-modal'); if(!modal){ debug('Modal not found!'); } table = document.getElementById('stream4chan-table'); if(!table){ debug('Table not found!'); } settings = document.getElementById('stream4chan-settings'); if(!settings){ debug('Settings not found!'); } content = document.getElementById('stream4chan-content'); if(!content){ debug('Content not found!'); } //Interactable Elements prevButton = document.getElementById('stream4chan-prev'); if(!prevButton){ debug('Previous Button not found!'); } nextButton = document.getElementById('stream4chan-next'); if(!nextButton){ debug('Next Button not found!'); } startButton = document.getElementById('stream4chan-start'); if(!startButton){ debug('Start Button could not be found!');} resumeButton = document.getElementById('stream4chan-resume'); if(!resumeButton){ debug('Resume Button could not be found!');} autoplay = document.getElementById('stream4chan-auto?'); if(!autoplay){ debug('Autoplay checkbox not found!');} playWebms = document.getElementById('stream4chan-webms?'); if(!playWebms){ debug('Player Webms input not found!');} playGifs = document.getElementById('stream4chan-gifs?'); if(!playGifs){ debug('Player Gifs input not found!');} loop = document.getElementById('stream4chan-loopAll?'); if(!loop){ debug('"loopAll" could not be found!'); } random = document.getElementById('stream4chan-random?'); if(!random){ debug('"random" could not be found!'); } shuffle = document.getElementById('stream4chan-shuffle?'); if(!shuffle){ debug('"shuffle" could not be found!'); } duration = document.getElementById('stream4chan-duration?'); if(!duration){ debug('Gif duration input not found!');} exit = document.getElementById('SFC-exit'); if(!exit){ debug('Exit not found!');} currentVideo = 'start'; currentTime = 0; currentTimeout = setTimeout(0); modalOn = false; shuffled = false; } function startEvents(){ //Window/Document Events magicMouse(); window.onresize = function(){ setDimensions(contentExists()); }; window.onclick = function(event){if (event.target.id == 'stream4chan-content'){ stop(); }}; //Key Down Events window.onkeydown = function(event){ event.target.blur(); if(!toggleDebug(event)){ //up arrow if(event.keyCode == 38 && modalOn){ duration.stepUp(); } //Down arrow if(event.keyCode == 40 && modalOn){ duration.stepDown(); } if (event.keyCode == 13 && event.shiftKey){ if(!modalOn){ start(currentVideo, currentTime); } }else if(event.keyCode == 13){ if(!modalOn){ start(); } } } }; //Key Up Events window.onkeyup = function(event){ event.target.blur(); if(modalOn){ //left arrow if(event.keyCode == 37){ previousVideo(); } //right arrow if(event.keyCode == 39){ nextVideo(); } //Escape key if(event.keyCode == 27){ stop(); } //W key if(event.keyCode == 87){ playWebms.checked = !playWebms.checked; } //G key if(event.keyCode == 71){ playGifs.checked = !playGifs.checked; } //A key if(event.keyCode == 65){ autoplay.checked = !autoplay.checked; applyAutoplay(); } //L key if(event.keyCode == 76){ loopAll.checked = !loopAll.checked; } //R key if(event.keyCode == 82){ random.checked = !random.checked; if(random.checked){ shuffleArr(); }else{ unshuffleArr(); } } //S key if(event.keyCode == 83){ if(shuffle.value === ' shuffle '){ shuffleArr(); }else{ unshuffleArr(); } } //Space key if(event.keyCode == 32){ if(contentExists('webm')){ if(hrefs[currentVideo].paused){ hrefs[currentVideo].play(); }else{ hrefs[currentVideo].pause(); } } } } }; //Page Element Events exit.onclick = function(){ stop(); }; startButton.onclick = function(){ start(); }; resumeButton.onclick = function(){ start(currentVideo, currentTime); }; autoplay.onclick = function(){ applyAutoplay(); }; random.onclick = function(){ if(random.checked){ shuffleArr(); }else{ unshuffleArr(); } }; prevButton.onclick = function(){ previousVideo(); }; nextButton.onclick = function(){ nextVideo(); }; shuffle.onclick = function(){ if(shuffle.value === ' unshuffle '){ unshuffleArr(); }else{ shuffleArr(); } }; } function displayModal(state){ //if state toggles modal if a state is not specified if(state === true){ debug('Show Modal'); modalOn = state; modal.style.display = 'block'; //removes scoll bar document.body.style.overflow = 'hidden'; }else if(state === false){ debug('Hide Modal'); modalOn = state; modal.style.display = 'none'; //removes scoll bar document.body.style.overflow = 'scroll'; }else{ if(!modalOn){ debug('Show Modal'); modal.style.display = 'block'; //removes scoll bar document.body.style.overflow = 'hidden'; }else{ debug('Hide Modal'); modal.style.display = 'none'; //removes scoll bar document.body.style.overflow = 'scroll'; } modalOn = !modalOn; } } function start(crntVid, crntTime){ var newStart = (!crntVid && (crntVid !== 0)); currentVideo = !newStart ? crntVid-1 : 'start'; currentTime = crntTime ? crntTime : 0; //resume if starting from the beginning if(newStart){ unshuffleArr(); } displayModal(true); content.innerHTML = ''; content.appendChild(spinner); //If document is loaded if (document.readyState === "complete") { nextVideo(); }else{ debug('page still loading'); window.onload = function(){ debug('page loaded'); if(modalOn){ nextVideo(); } }; } } function stop(){ if(contentExists()){ currentTime = contentExists('webm') ? contentExists('webm').currentTime : 0; pauseContent(); } content.innerHTML = ''; displayModal(false); } function nextVideo(){ debug('Function: nextVideo'); pauseContent(); if(currentVideo === 'start'){ currentVideo = -1; } //While the href can't be played do{ currentVideo++; if(loop.checked){ currentVideo = currentVideo % hrefs.length; } var temp = noContentToDisplay(); if(temp){ stop(); displayModal(true); content.innerHTML = temp; return; } }while(!canPlay(hrefs[currentVideo])); //Play specified video/img element playContent(hrefs[currentVideo]); } function previousVideo(){ debug('Function: previousVideo'); pauseContent(); if(currentVideo === 'start'){ currentVideo = 0; } //While the href can't be played do{ currentVideo--; if(loop.checked){ if(currentVideo === -1){ currentVideo = hrefs.length -1;} } var temp = noContentToDisplay(); if(temp){ stop(); displayModal(true); content.innerHTML = temp; return; } }while(!canPlay(hrefs[currentVideo])); //Play specified video/img element playContent(hrefs[currentVideo]); } function playContent(currentContent){ debug('Function: playContent'); if(random.checked){ shuffleArr(); } content.innerHTML = ''; content.appendChild(spinner); //Play specified video/img element //If its a webm, play when loaded, otherwise play immediately if(getFileExt(currentContent.src) === 'webm' ){ if(currentContent.canStart){ showContent(); //add events for next video applyAutoplay(); }else{ currentContent.oncanplaythrough = function(){ x.oncanplaythrough = function(){ this.canStart = true; }; showContent(); //add events for next video applyAutoplay(); }; } }else{ showContent(); //add events for next video applyAutoplay(); } } function showContent(){ content.innerHTML = ''; content.appendChild(hrefs[currentVideo]); setDimensions(contentExists()); if(contentExists('webm')){ contentExists('webm').currentTime = currentTime; currentTime = 0; contentExists('webm').play(); } } function pauseContent(){ debug('Function: pauseContent'); var x = contentExists('webm'); if(x){ x.removeEventListener('ended', nextVideo, false); x.loop = true; x.pause(); } clearTimeout(currentTimeout); content.appendChild(spinner); } function canPlay(currentContent){ return ( (playGifs.checked && (getFileExt(currentContent.src) === 'gif')) || (playWebms.checked && (getFileExt(currentContent.src) === 'webm')) ); } function getFileExt(input){ temp = input.toString(); return temp.substr(temp.lastIndexOf('.') + 1); } function preload(arr){ debug('Function: preload'); var temp = document.getElementsByClassName('fileThumb'); var x = null; //Iterates over number of gif/webms in a thread for (i = 0; i < temp.length; i++){ debug(' intitializing element: ' + temp[i]); //If (gif){ //} else if (webm){ //} else {} if(getFileExt(temp[i]) == 'gif'){ x = document.createElement('img'); x.setAttribute("id", "stream4chan-gif"); }else if (getFileExt(temp[i]) == 'webm'){ x = document.createElement('video'); x.setAttribute("id", "stream4chan-webm"); x.setAttribute("controls",""); x.setAttribute("loop",""); x.loop = true; x.setAttribute("autoplay",""); x.autoplay = false; x.setAttribute("ended",""); x.ended = false; x.setAttribute("preload", ""); x.preload = 'auto'; x.setAttribute("canStart", "false"); x.canStart = false; x.oncanplaythrough = function(){ this.canStart = true; }; } x.setAttribute("src",temp[i]); x.setAttribute("style", "height: 100%; width: auto;"); arr.push(x); backup.push(x); } } function debug(text){ if(debugOn){ console.log(text); }else{ hiddenPrints.push(text); } } function toggleDebug(e) { var evtobj = window.event? event : e; if (!e || evtobj.keyCode == 68 && evtobj.ctrlKey && evtobj.altKey && evtobj.shiftKey){ debug('Function: toggleDebug'); if(debugOn){ console.log('##Debug Off##'); debugOn = false; }else{ console.log('##Debug On##'); debugOn = true; for(var i = 0; i < hiddenPrints.length; i++){ debug(hiddenPrints[i]); } hiddenPrints = []; } return true; } return false; } function setDimensions(e){ debug('Function: setDimensions'); content.style.height = window.innerHeight - (settings.offsetHeight*2) + 'px'; if(e){ e.style.height = '100%'; e.style.width = 'auto'; if(e.offsetWidth > (window.innerWidth - (prevButton.offsetWidth + nextButton.offsetWidth))){ debug(' Content was wider'); e.style.height = 'auto'; e.style.width = '100%'; } e.style.marginTop = ((content.offsetHeight - e.offsetHeight)/2) + 'px'; e.style.marginBottom = ((content.offsetHeight - e.offsetHeight)/2) + 'px'; } } function contentExists(type){ debug('Function: contentExists'); if(type){ return document.getElementById('stream4chan-' + type); }else{ var div = document.getElementById('stream4chan-webm'); if(!div){ div = document.getElementById('stream4chan-gif'); } return div; } } function thereAre(type){ debug('Function: thereAre'); if(type){ for(i = 0; i < hrefs.length; i++){ if(getFileExt(hrefs[i].src) === type){ return true; } } } debug(' Error: argument not passed to function'); return false; } function noContentToDisplay(){ debug('Function: noContentToDisplay'); var text = false; //End and display modal if nothing can play if(!playGifs.checked && !playWebms.checked){ text = 'Nothing selected to play'; } //End and display modal if nothing can play if(!thereAre('gif') && (playGifs.checked && !playWebms.checked)){ text = 'No gifs!'; } //End and display modal if nothing can play if(!thereAre('webm') && (!playGifs.checked && playWebms.checked)){ text = 'No Webms!'; } //End and display modal if nothing can play if(!thereAre('gif') && !thereAre('webm')){ text = 'No Gifs or Webs'; } //End and display modal if out of range if( 0 > currentVideo || currentVideo > hrefs.length-1){ text = 'End of Thread'; if(currentVideo < 0){ text = 'Start of thread'; } } if(text){ debug(' Returning: '+ text); } return text; } function applyAutoplay(){ debug('Function: applyAutoplay'); var x = contentExists(); debug(' ' + getFileExt(x.src) + ' event listener is ' + autoplay.checked); clearTimeout(); if(getFileExt(x.src) === 'webm'){ x.loop = !autoplay.checked; x.removeEventListener('ended', nextVideo, false); if(autoplay.checked){ x.addEventListener('ended', nextVideo, false); } } if(getFileExt(x.src) === 'gif' && autoplay.checked){ currentTimeout = setTimeout(nextVideo, duration.value*1000); } } function shuffleArr() { debug('Function: shuffleArr'); var j, x, i; for (i = hrefs.length; i; i--) { j = Math.floor(Math.random() * i); x = hrefs[i - 1]; hrefs[i - 1] = hrefs[j]; hrefs[j] = x; } shuffled = true; shuffle.value = ' unshuffle '; } function unshuffleArr() { debug('Function: unshuffleArr'); hrefs = backup.slice(); if(contentExists()){ for(var i = 0; i < hrefs.length; i++){ if(hrefs[i].src === contentExists().src){ currentVideo = i; break; } } } shuffled = false; shuffle.value = ' shuffle '; } function magicMouse() { var mouseTimer = null, cursorVisible = true; document.onmousemove = function() { if (mouseTimer) { window.clearTimeout(mouseTimer); } if (!cursorVisible) { debug('adding cursor'); content.style.cursor = 'auto'; cursorVisible = true; } mouseTimer = window.setTimeout(function(){ debug('removing cursor'); mouseTimer = null; content.style.cursor = 'none'; cursorVisible = false; }, 1500); }; }