-
-
Save Mordo95/141ead6ffa1fb6557db127d744a39e6f to your computer and use it in GitHub Desktop.
| // ==UserScript== | |
| // @name Video Downloader for Tampermonkey | |
| // @version 0.4 | |
| // @description Will add a download button to Reddit, Facebook and Youtube videos | |
| // @author Github/Mordo95 | |
| // @match *://*/* | |
| // @grant GM_xmlhttpRequest | |
| // @run-at document-start | |
| // ==/UserScript== | |
| // version 0.4 fixes reddit thumbnails also receiving a button | |
| (function() { | |
| 'use strict'; | |
| class Mordo95DL { | |
| static addStyle(css) { | |
| const style = document.getElementById("GM_addStyleBy8626") || (function() { | |
| const style = document.createElement('style'); | |
| style.type = 'text/css'; | |
| style.id = "GM_addStyleBy8626"; | |
| document.head.appendChild(style); | |
| return style; | |
| })(); | |
| const sheet = style.sheet; | |
| sheet.insertRule(css, (sheet.rules || sheet.cssRules || []).length); | |
| } | |
| static paramsToObject(entries) { | |
| const result = {} | |
| for(const [key, value] of entries) { // each 'entry' is a [key, value] tupple | |
| result[key] = value; | |
| } | |
| return result; | |
| } | |
| static buildParams(p) { | |
| return new URLSearchParams(p).toString(); | |
| } | |
| } | |
| class FBDL { | |
| getReactFiber(el) { | |
| for (let prop of Object.keys(el)) { | |
| if (prop.startsWith("__reactFiber")) { | |
| return el[prop]; | |
| } | |
| } | |
| return null; | |
| } | |
| fiberReturnUntil(fiber, displayName) { | |
| let fiberInst = fiber; | |
| while (fiberInst != null) { | |
| let fiberInstName = ""; | |
| if (typeof fiberInst.elementType === "string") | |
| fiberInstName = fiberInst.elementType; | |
| else if (typeof fiberInst.elementType === "function") | |
| fiberInstName = fiberInst.elementType.displayName; | |
| if (fiberInstName === displayName) | |
| return fiberInst; | |
| fiberInst = fiberInst.return; | |
| } | |
| return null; | |
| } | |
| parentsUntil(el, c) { | |
| let elInst = el; | |
| while (elInst != null) { | |
| if (elInst.classList.toString() === c) | |
| return elInst; | |
| elInst = elInst.parentElement; | |
| } | |
| return null; | |
| } | |
| getVideoImplementation(fiber, impl = "VideoPlayerProgressiveImplementation") { | |
| if(!fiber || !fiber.memoizedProps || !fiber.memoizedProps.implementations) | |
| return null; | |
| return fiber.memoizedProps.implementations.find(x => x.typename === impl); | |
| } | |
| addVideoButton(on, videoEl) { | |
| let btn = document.createElement("div"); | |
| btn.innerHTML = "Download (HD)"; | |
| btn.classList.add("dlBtn"); | |
| btn.onclick = () => this.btnAct(videoEl); | |
| //let parent = parentsUntil(videoEl, videoEl.classList[0]) || videoEl.parentElement; | |
| on.prepend(btn); | |
| } | |
| btnAct(videoEl) { | |
| let fiber = this.getReactFiber(videoEl); | |
| let props = this.fiberReturnUntil(fiber, "a [from CoreVideoPlayer.react]"); | |
| let impl = this.getVideoImplementation(props); | |
| if (impl.data.hdSrc) { | |
| window.open(impl.data.hdSrc); | |
| } else { | |
| window.open(impl.data.sdSrc); | |
| } | |
| } | |
| inject() { | |
| setInterval(() => { | |
| let videos = document.querySelectorAll("video:not([data-tagged])"); | |
| for (let video of videos) { | |
| video.setAttribute("data-tagged", "true"); | |
| let fiber = this.getReactFiber(video.parentElement); | |
| let props = this.fiberReturnUntil(fiber, "a [from CoreVideoPlayer.react]"); | |
| this.addVideoButton(document.querySelector(`[data-instancekey='${props.memoizedState.memoizedState}']`), video.parentElement); | |
| } | |
| }, 200); | |
| Mordo95DL.addStyle(".dlBtn{position:absolute;top:0;right:0;z-index:99999;padding:10px 15px;margin:5px;cursor:pointer;outline:0;background:var(--primary-button-background);color:var(--primary-button-text);border:1px solid 1px solid var(--accent);font-family: var(--font-family-segoe)!important}"); | |
| Mordo95DL.addStyle(".dlBtn:hover{background-color:var(--primary-button-pressed)}"); | |
| } | |
| } | |
| class YTDL { | |
| addVideoButton(on) { | |
| let btn = document.createElement("div"); | |
| btn.innerHTML = this.btnText; | |
| btn.classList.add("dlBtn"); | |
| btn.onclick = () => this.getLinks(btn); | |
| on.prepend(btn); | |
| } | |
| getLinks(btn) { | |
| let fd = new FormData(); | |
| fd.set("q", window.location.href); | |
| fd.set("vt", "mp4"); | |
| let url = "https://yt1s.com/api/ajaxSearch/index"; | |
| GM_xmlhttpRequest({ | |
| method: 'POST', | |
| url, | |
| data: fd, | |
| onload: (resp) => { | |
| let js = JSON.parse(resp.responseText); | |
| this.convert(btn, js.vid, js.links.mp4.auto.k); | |
| } | |
| }); | |
| } | |
| convert(btn, vid, k) { | |
| let fd = new FormData(); | |
| fd.set("vid", vid); | |
| fd.set("k", k); | |
| btn.innerHTML = "Converting ..."; | |
| GM_xmlhttpRequest({ | |
| method: 'POST', | |
| url: 'https://yt1s.com/api/ajaxConvert/convert', | |
| data: fd, | |
| timeout: 60000, | |
| onload: (resp) => { | |
| let js = JSON.parse(resp.responseText); | |
| let status = js.c_status; | |
| if (status === "CONVERTED") { | |
| window.open(js.dlink); | |
| } else { | |
| alert("Error converting video. Please try again later!"); | |
| } | |
| btn.innerHTML = this.btnText; | |
| }, | |
| onTimeout: () => { btn.innerHTML = this.btnText; } | |
| }); | |
| } | |
| inject() { | |
| setInterval(() => { | |
| let videos = document.querySelectorAll("video:not([data-tagged])"); | |
| for (let video of videos) { | |
| video.setAttribute("data-tagged", "true"); | |
| console.log(document.querySelector("#container")); | |
| this.addVideoButton(document.querySelector("#container.ytd-player")); | |
| } | |
| }, 200); | |
| Mordo95DL.addStyle(".dlBtn{position:absolute;top:0;right:0;z-index:99999;padding:10px 15px;margin:5px;cursor:pointer;outline:0;background:#5383FB;color:white;border:1px solid 1px solid #5383FB;font-family: Segoe UI Historic, Segoe UI, Helvetica, Arial, sans-serif !important;font-size:12px;}"); | |
| Mordo95DL.addStyle(".dlBtn:hover{background-color:#86A4FC}"); | |
| } | |
| constructor() { | |
| this.btnText = "Download (HD)"; | |
| } | |
| } | |
| class RDDL { | |
| addVideoButton(on, videoEl) { | |
| on.querySelectorAll(".dlBtn").forEach(el => el.remove()); | |
| let btn = document.createElement("div"); | |
| btn.innerHTML = this.btnText; | |
| btn.classList.add("dlBtn"); | |
| btn.onclick = () => this.btnAct(btn); | |
| on.prepend(btn); | |
| } | |
| btnAct(btn) { | |
| let src = this.returnUntil(this.getReactInternalState(btn.parentElement), "mpegDashSource"); | |
| if (!src) { | |
| alert("Unable to load video data"); | |
| return; | |
| } | |
| let mpegDashUrl = src.pendingProps.mpegDashSource; | |
| let match = mpegDashUrl.match(/https:\/\/v.redd.it\/(?<videoId>.+)\/DASHPlaylist\.mpd/); | |
| if (!match) { | |
| alert("Unable to load video data"); | |
| return; | |
| } | |
| let videoId = match.groups.videoId; | |
| let p = Mordo95DL.buildParams({ | |
| video_url: 'https://v.redd.it/' + videoId + '/DASH_720.mp4?source=fallback', | |
| audio_url: 'https://v.redd.it/' + videoId + '/DASH_audio.mp4?source=fallback', | |
| permalink: window.location.origin + src.pendingProps.postUrl.pathname | |
| }); | |
| window.open("https://ds.redditsave.com/download.php?" + p); | |
| } | |
| getReactInternalState(el) { | |
| for (let prop of Object.keys(el)) { | |
| if (prop.startsWith("__reactInternalInstance")) { | |
| return el[prop]; | |
| } | |
| } | |
| return null; | |
| } | |
| returnUntil(inst, prop) { | |
| let fInst = inst; | |
| while (fInst != null) { | |
| if (fInst.pendingProps[prop]) | |
| return fInst; | |
| fInst = fInst.return; | |
| } | |
| return null; | |
| } | |
| inject() { | |
| setInterval(() => { | |
| let videos = document.querySelectorAll("video:not([data-tagged])"); | |
| for (let video of videos) { | |
| if (video.parentElement.querySelector(".dlBtn") == null && video.parentElement.parentElement.firstChild.getAttribute("role") !== "slider") | |
| this.addVideoButton(video.parentElement); | |
| } | |
| }, 200); | |
| Mordo95DL.addStyle(".dlBtn{position:absolute;top:0;right:0;z-index:99999;padding:10px 15px;margin:5px;cursor:pointer;outline:0;background:#5383FB;color:white;border:1px solid 1px solid #5383FB;font-family: Segoe UI Historic, Segoe UI, Helvetica, Arial, sans-serif !important;font-size:12px;}"); | |
| Mordo95DL.addStyle(".dlBtn:hover{background-color:#86A4FC}"); | |
| } | |
| constructor() { | |
| this.btnText = "Download (HD)"; | |
| } | |
| } | |
| document.addEventListener('DOMContentLoaded', () => { | |
| if (window.location.href.match(/youtu(\.)?be.*/)) { new YTDL().inject(); } | |
| if (window.location.href.match(/facebook\..*/)) { new FBDL().inject(); } | |
| if (window.location.href.match(/reddit\..*/)) { new RDDL().inject(); } | |
| }, false); | |
| })(); |
Works on Facebook. Dont works on Youtube. Didnt tested on Reddit. Anyway, why dont you publish it on Greasyfork? Can I publish it (and give you credits)?
Yeah youtube keeps changing their player protocol so I have to put it in a different position. I also concidentially found out that the provider I use is blocked in certain countries so I will change that. And I didn't think of that, will look into publishing it there, thanks for the tip!
You did a good work, it will help a lot o people.
Good job. In the case of Facebook reels, the button is not clickable. If you insert the button before the div 8 levels above the video just before the div with class=xjbqb8w...x1m6m0jg then it is positioned at a convenient point on the screen and clickable.
Good job. In the case of Facebook reels, the button is not clickable. If you insert the button before the div 8 levels above the video just before the div with class=xjbqb8w...x1m6m0jg then it is positioned at a convenient point on the screen and clickable.
Hi, this script hasn't been updated for a while. Check out https://github.com/Mordo95/video-downloader for a more recent version
Yeah youtube keeps changing their player protocol so I have to put it in a different position. I also concidentially found out that the provider I use is blocked in certain countries so I will change that. And I didn't think of that, will look into publishing it there, thanks for the tip!