NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Youtube Teater // @namespace http://tampermonkey.net/ // @version 1.0 // @license MIT // @description New Look For Youtube! // @run-at document-idle // @author You // @match https://www.youtube.com/watch?* // @grant none // ==/UserScript== class YoutubeVideo { constructor(id, title, imgUrl) { this.id = id; this.title = title; this.image = new Image(); this.image.src = imgUrl; } set id(value) { this._id = value; } get id() { return this._id; } set title(value) { this._title = value; } get title() { return this._title; } set image(value) { if (value instanceof Image) this._image = value; } get image() { return this._image; } } ////////////////////////////////////////////////////////////////////////////// // Library ////////////////////////////////////////////////////////////////////////////// function elem(tag, ...args) { if (typeof tag !== 'string') return; let [e, id, ...classes] = [document.createElement(tag), ...args]; if (id) e.id = id; if (classes) e.setAttribute('class', classes.reduce((a, b) => a + " " + b, "")); return e; } function div(...args) { return elem('div', ...args); } function ul(...args) { return elem('ul', ...args); } function li(...args) { return elem('li', ...args); } function a(...args) { return elem('a', ...args); } ////////////////////////////////////////////////////////////////////////////// // Stuff ////////////////////////////////////////////////////////////////////////////// async function load(url) { let res = await fetch(url); return await res.text(); } function getVideos(source) { return Promise.all(source.match(/(?<="compactVideoRenderer":{"videoId":").*?(?="},"longBylineText")/g)); } function getData(matches) { let map = new Map; matches.forEach((m, i) => { m = m.replace(/\\u0026/g, "&"); let id = m.split("\",\"")[0]; let title = m.match(/(?<="simpleText":").+/g)[0]; let thumbnail = m.match(/(?<="url":")https.+?(?=")/g)[1]; // if(i===0) console.log(m); map.set(id, new YoutubeVideo(id, title, thumbnail)); }); return Promise.resolve(map); } function renderSuggestedVideos(container) { return function (videos) { videos.forEach(video => { let list = li(null, 'video-list'); container.appendChild(list); // Clickable Link let videoLink = a(null, 'video-link'); videoLink.href = `watch?v=${video.id}`; list.appendChild(videoLink); let holder = div(null, 'video-list-grid'); videoLink.appendChild(holder); // Video Image let image = video.image; image.setAttribute('class', 'video-thumbnail'); holder.appendChild(image); // Video Title let divTitle = div(null, 'video-desc'); divTitle.innerText = video.title; holder.appendChild(divTitle); }); } } ////////////////////////////////////////////////////////////////////////////// // Main Stuff // ////////////////////////////////////////////////////////////////////////////// let showControlsTimer; let nextVideoTimer; let frameHeight = 360 + 26; let frameWidth = 640 + 26; let style1 = ` .wrapper { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 18px 0px; } div.video-frame { width: 680px; height: 480px; background-image: url(https://az414406.vo.msecnd.net/img2/tv-transparent.svg); background-size: cover; } video { position: relative; top: 14px; left: 13px; width: 655px; height: 409px; background-color: #2C3F4F; } ul.video-list { list-style: none; display: inline-grid; } ul.video-list li { line-height: 110px; margin: 10px 0px; } a > *.video-list-grid { display: grid; grid-template-areas: "thumbnail desc"; } img.video-thumbnail { width: 196px; height: 110px; border-radius: 10px; } div.video-desc { font-size: 22px; padding-left: 20px; padding: 10px; } form#vsearch { width: 100%; margin: 10px auto; text-align: center; } form#vsearch input{ padding: 8px; font-size: 16px; border-radius: 8px; } form#vsearch > input[name=q]{ width: 60%; } div.toggle-button-container { display: flex; flex-direction: row; align-items: center; } div.toggle-button { width: 100px; height: 30px; border: 1px solid #9e9e9; border-radius: 30px; background-color: #dedede; padding: 5px; margin-left: 30px; transition: all 0.4s; } div.toggle-button.slider { width: 40px; height: 40px; background-color: gray; position: relative; top: -10px; left: -10px; margin: 0px; } div.toggle-button.slider[toggled] { width: 40px; height: 40px; background-color: blue; position: relative; top: -10px; left: 60px; margin: 0px; } `; (async function () { 'use strict'; let textDuration = document.body.innerHTML.substring(document.body.innerHTML.indexOf("ytp-time-duration") + 19).split("<")[0]; //document.body.innerHTML.substring(document.body.innerHTML.indexOf("duration=")).split(" ")[0].split("=")[1].replace(/\"/g,""); for (let prop in window) { if (typeof window[prop] === 'function') { clearInterval(window[prop]); } } let title = document.querySelector('title').innerText; let video = document.querySelector('video'); video.removeAttribute('class'); video.removeAttribute('style'); video.controlsList.toggle('nodownload'); video.download = true; let update = function () { if (!video.webkitDisplayingFullscreen) { video.controls = true; video.removeAttribute('controlslist'); video.removeAttribute('tabindex'); video.removeAttribute('style'); } requestAnimationFrame(update); } showControlsTimer = setTimeout(update, 100); let source = await load(window.location); document.querySelector('html').style = ''; document.head.innerHTML = `<title>${title}</title>`; document.body.innerHTML = `<style>${style1}</style><div class="wrapper"> <form id="vsearch" action="results?"> <input name="q" type="text" placeholder="Search Youtube Videos"/> <input type="submit" value="Search"/> </form> <div class="video-frame"> </div> <div id="autoplay" class="toggle-button-container"> <span>Autoplay</span> <div class="toggle-button"> <div class="toggle-button slider"></div> </div> </div> <table class="list"><tbody></tbody></table></div>`; let wrapper = document.querySelector('.wrapper'); let container = wrapper.querySelector('.video-frame'); container.appendChild(video); video.play(); // Video List Stuff const videoListHolder = div(); wrapper.appendChild(videoListHolder); const videoList = ul(null, 'video-list'); videoListHolder.appendChild(videoList); let nextVideo = null; getVideos(source).then(getData).then(videos => { renderSuggestedVideos(videoList)(videos); nextVideo = videos.entries().next().value[1]; }); let time = textDuration.split(/:/g); let s = time.map(t => parseInt(t)).reduce((a, b, i, arr) => a + b * Math.pow(60, arr.length - i - 1), 0); let ignoredTime = isNaN(s); console.log(`Text Time: ${textDuration}, Time: ${time.reduce((a,b)=>a+":"+b)}, Video Length: ${s} seconds`); // Autoplay next video let checkForNextVideo = function () { let duration = video.getDuration() | 0 + 1; if (ignoredTime || Math.abs(duration - s) > 1) return; if (video.getDuration() - video.getCurrentTime() < 0.01) { if (typeof nextVideo !== 'undefined') { let a = document.createElement("a"); a.href = `watch?v=${nextVideo.id}`; a.click(); } } } document.querySelectorAll("div.toggle-button-container").forEach(container => { let toggle_button = container.querySelector("div.toggle-button.slider"); container.addEventListener("click", function (event) { if (toggle_button.hasAttribute("toggled")) { toggle_button.removeAttribute("toggled"); localStorage.setItem("autoplay", "0"); try { toggle_button.onuntoggled(event); } catch (e) {} } else { toggle_button.setAttribute("toggled", ""); localStorage.setItem("autoplay", "1"); try { toggle_button.ontoggled(event); } catch (e) {} } }); toggle_button.ontoggled = function (e) { nextVideoTimer = setInterval(checkForNextVideo, 100); } toggle_button.onuntoggled = function (e) { clearInterval(nextVideoTimer); } }); // Setup autoplay and its toggle button to match localStorage if it exists let autoplay = localStorage.getItem("autoplay"); if (autoplay !== null) { let toggle_button = document.querySelector("div#autoplay div.toggle-button.slider"); try { if (autoplay === "1") { toggle_button.setAttribute("toggled", ""); localStorage.setItem("autoplay", "1"); try { toggle_button.ontoggled(event); } catch (e) {} } else if (autoplay === "0") { toggle_button.removeAttribute("toggled"); localStorage.setItem("autoplay", "0"); try { toggle_button.onuntoggled(event); } catch (e) {} } } catch (e) {} } else { localStorage.setItem("autoplay", "0"); } })();