Skip to content

Instantly share code, notes, and snippets.

@Mordo95
Last active February 20, 2024 23:39
Show Gist options
  • Select an option

  • Save Mordo95/141ead6ffa1fb6557db127d744a39e6f to your computer and use it in GitHub Desktop.

Select an option

Save Mordo95/141ead6ffa1fb6557db127d744a39e6f to your computer and use it in GitHub Desktop.
Tampermonkey script to add a download button to facebook, reddit and youtube videos
// ==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);
})();
@Korb

Korb commented Mar 11, 2022

Copy link
Copy Markdown

Script not recognized as compatible with Tampermonkey 4.15.6154 (9 March 2022). Mozilla Firefox 98.0 (64-bit).

@Mordo95

Mordo95 commented Mar 11, 2022

Copy link
Copy Markdown
Author

Script not recognized as compatible with Tampermonkey 4.15.6154 (9 March 2022). Mozilla Firefox 98.0 (64-bit).

Hi Korb, I didn't test it with Firefox, I made it speficially on Chrome.

@Informatheus

Copy link
Copy Markdown

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)?

@Mordo95

Mordo95 commented Oct 18, 2022

Copy link
Copy Markdown
Author

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!

@Informatheus

Informatheus commented Oct 18, 2022

Copy link
Copy Markdown

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.

@fezzzza

fezzzza commented Jan 13, 2023

Copy link
Copy Markdown

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.

@Mordo95

Mordo95 commented Jan 13, 2023

Copy link
Copy Markdown
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment