Adds a download button to tiktok.com/@profile/video pages. Click a video, the icon is added by the social media share buttons. Right click the icon, and save as. Left click takes you to the video in your browser.
// ==UserScript== // @name TikTok Video Downloader // @namespace https://www.laconicdesigns.com // @version 0.1 // @description Adds a download button to tiktok.com/@profile/video pages. Click a video, the icon is added by the social media share buttons. Right click the icon, and save as. Left click takes you to the video in your browser. // @author Blake B // @match https://www.tiktok.com/@* // @grant none // ==/UserScript== (function() { 'use strict'; function showDownloadButton(url_to_download) { if (document.body.contains(document.getElementById("blake_dl_btn")) == true) { document.getElementByid("blake_dl_btn").remove(); //It pulls 2 links at a time because of the one on the main page. } var elem_button = document.createElement("A"); //Anchor tag elem_button.setAttribute("href", url_to_download); //Download URL elem_button.setAttribute("id", "blake_dl_btn"); //So we can find it later //This is the icon. It's the easiest way to do this, and keep it shareable. elem_button.innerHTML = "<img style=\"max-width: 20px;\" src=\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE2LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPg0KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCINCgkgd2lkdGg9IjQzOC41MzNweCIgaGVpZ2h0PSI0MzguNTMzcHgiIHZpZXdCb3g9IjAgMCA0MzguNTMzIDQzOC41MzMiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQzOC41MzMgNDM4LjUzMzsiDQoJIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPGc+DQoJPGc+DQoJCTxwYXRoIGQ9Ik00MDkuMTMzLDEwOS4yMDNjLTE5LjYwOC0zMy41OTItNDYuMjA1LTYwLjE4OS03OS43OTgtNzkuNzk2QzI5NS43MzYsOS44MDEsMjU5LjA1OCwwLDIxOS4yNzMsMA0KCQkJYy0zOS43ODEsMC03Ni40Nyw5LjgwMS0xMTAuMDYzLDI5LjQwN2MtMzMuNTk1LDE5LjYwNC02MC4xOTIsNDYuMjAxLTc5LjgsNzkuNzk2QzkuODAxLDE0Mi44LDAsMTc5LjQ4OSwwLDIxOS4yNjcNCgkJCWMwLDM5Ljc4LDkuODA0LDc2LjQ2MywyOS40MDcsMTEwLjA2MmMxOS42MDcsMzMuNTkyLDQ2LjIwNCw2MC4xODksNzkuNzk5LDc5Ljc5OGMzMy41OTcsMTkuNjA1LDcwLjI4MywyOS40MDcsMTEwLjA2MywyOS40MDcNCgkJCXM3Ni40Ny05LjgwMiwxMTAuMDY1LTI5LjQwN2MzMy41OTMtMTkuNjAyLDYwLjE4OS00Ni4yMDYsNzkuNzk1LTc5Ljc5OGMxOS42MDMtMzMuNTk2LDI5LjQwMy03MC4yODQsMjkuNDAzLTExMC4wNjINCgkJCUM0MzguNTMzLDE3OS40ODUsNDI4LjczMiwxNDIuNzk1LDQwOS4xMzMsMTA5LjIwM3ogTTM1My43NDIsMjk3LjIwOGMtMTMuODk0LDIzLjc5MS0zMi43MzYsNDIuNjMzLTU2LjUyNyw1Ni41MzQNCgkJCWMtMjMuNzkxLDEzLjg5NC00OS43NzEsMjAuODM0LTc3Ljk0NSwyMC44MzRjLTI4LjE2NywwLTU0LjE0OS02Ljk0LTc3Ljk0My0yMC44MzRjLTIzLjc5MS0xMy45MDEtNDIuNjMzLTMyLjc0My01Ni41MjctNTYuNTM0DQoJCQljLTEzLjg5Ny0yMy43OTEtMjAuODQzLTQ5Ljc3Mi0yMC44NDMtNzcuOTQxYzAtMjguMTcxLDYuOTQ5LTU0LjE1MiwyMC44NDMtNzcuOTQzYzEzLjg5MS0yMy43OTEsMzIuNzM4LTQyLjYzNyw1Ni41MjctNTYuNTMNCgkJCWMyMy43OTEtMTMuODk1LDQ5Ljc3Mi0yMC44NCw3Ny45NDMtMjAuODRjMjguMTczLDAsNTQuMTU0LDYuOTQ1LDc3Ljk0NSwyMC44NGMyMy43OTEsMTMuODk0LDQyLjYzNCwzMi43MzksNTYuNTI3LDU2LjUzDQoJCQljMTMuODk1LDIzLjc5MSwyMC44MzgsNDkuNzcyLDIwLjgzOCw3Ny45NDNDMzc0LjU4LDI0Ny40MzYsMzY3LjYzNywyNzMuNDE3LDM1My43NDIsMjk3LjIwOHoiLz4NCgkJPHBhdGggZD0iTTMxMC42MzMsMjE5LjI2N0gyNTUuODJWMTE4Ljc2M2MwLTIuNjY2LTAuODYyLTQuODUzLTIuNTczLTYuNTY3Yy0xLjcwNC0xLjcwOS0zLjg5NS0yLjU2OC02LjU1Ny0yLjU2OGgtNTQuODIzDQoJCQljLTIuNjY0LDAtNC44NTQsMC44NTktNi41NjcsMi41NjhjLTEuNzE0LDEuNzE1LTIuNTcsMy45MDEtMi41Nyw2LjU2N3YxMDAuNWgtNTQuODE5Yy00LjE4NiwwLTcuMDQyLDEuOTA1LTguNTY2LDUuNzA5DQoJCQljLTEuNTI0LDMuNjIxLTAuODU0LDYuOTQ3LDEuOTk5LDkuOTk2bDkxLjM2Myw5MS4zNjFjMi4wOTYsMS43MTEsNC4yODMsMi41NjcsNi41NjcsMi41NjdjMi4yODEsMCw0LjQ3MS0wLjg1Niw2LjU2OS0yLjU2Nw0KCQkJbDkxLjA3Ny05MS4wNzNjMS45MDItMi4yODMsMi44NTEtNC41NzYsMi44NTEtNi44NTJjMC0yLjY2Mi0wLjg1NS00Ljg1My0yLjU3My02LjU3DQoJCQlDMzE1LjQ4OSwyMjAuMTIyLDMxMy4yOTksMjE5LjI2NywzMTAuNjMzLDIxOS4yNjd6Ii8+DQoJPC9nPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPC9zdmc+DQo=\" />"; //This needs to be set to an svg download icon var share_buttons = document.getElementsByClassName("_share_content"); share_buttons[share_buttons.length - 1].appendChild(elem_button); } var element_to_observe = document.body; //I Watch Everything... var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.addedNodes.length == 1) { var top_node = mutation.addedNodes[0]; // The added node var vid_node_total = top_node.getElementsByTagName("VIDEO").length; // Looking for <video> tags if (vid_node_total > 0) { //Found some // console.log("Vid Nodes Found: " + vid_node_total); //This many var vid_node = top_node.getElementsByTagName("VIDEO")[0]; // Got whole element if (vid_node) { var video_source = vid_node.getAttribute("src"); // <video src="this"> // console.log("Showing Download Button With a href of: " + video_source); showDownloadButton(video_source); // Show the download icon. } } } else if (mutation.addedNodes.length > 1) { console.log("More than 1 node added."); } }); }); console.log("Starting Observation..."); observer.observe(element_to_observe, { childList: true, subtree: true }); })();