everythingdd

Tiktok-Downloader 1.0.3

Sep 4th, 2021 (edited)
2,409
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         Tiktok-Downloader
  3. // @version      1.0.4
  4. // @description  tiktok.com Video Downloader
  5. // @match        https://www.tiktok.com/*
  6. // @match        https://tiktok.com/*
  7. // @grant        GM_download
  8. // @grant        GM_addStyle
  9. // @grant        GM_xmlhttpRequest
  10. // @connect      tiktok.com
  11. // @connect      api-t2.tiktokv.com
  12. // @icon         https://s16.tiktokcdn.com/musical/resource/wap/static/image/logo_144c91a.png?v=2
  13. // @original     https://greasyfork.org/users/209098
  14. // @license      MIT
  15. // ==/UserScript==
  16.  
  17. (function () {
  18.     'use strict'
  19.  
  20.     GM_addStyle(
  21.         ".card-button, .card-hires-button { position: absolute; top: 20px; width: 40px; height: 40px; font-size: 15px; line-height: 50%; border: none; " +
  22.         "background: #4f3f39; color: #FFF; cursor: pointer; border-radius: 100%; transition: opacity 0.4s; } .card-button:disabled, .card-hires-button:disabled { opacity: 0.01; background-color: #c0392b; } " +
  23.         ".vid_height_add { top: 78px !important; width: 40px !important; height: 40px !important;}"+
  24.  
  25.         ".card-hires-button:not(.feed-hires-button){ margin-left: 50px; line-height: normal;}"+
  26.  
  27.         ".card-button.feed-button, .card-hires-button.feed-hires-button { width: 48px; height: 48px; top: -50px; background: #f1f1f3; color: #171717; }"+
  28.  
  29.         ".card-button.feed-button:hover, .card-hires-button.feed-hires-button:hover{ background: #e7e7e9; }"+
  30.  
  31.         ".card-hires-button.feed-hires-button { margin-top: -55px; line-height: normal;}"+
  32.  
  33.         ".logo { display: none !important; } .mute-icon { opacity: 1 !important; } "
  34.     );
  35.  
  36.     /*///////////////////////////////////
  37.           WORKING AS OF 07/02/2021
  38.     /*///////////////////////////////////
  39.     function generateBase62(length) {
  40.         var i = 0, r = "", base62 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  41.         while (i < length) {
  42.             r += base62.charAt(Math.floor(Math.random() * 62));
  43.             i++;
  44.         }
  45.         return r;
  46.     }
  47.  
  48.     function getQualityUri(value) {
  49.         var i = value.indexOf("?video_id="), e;
  50.         if (i > -1 && (e = value.indexOf("&", i + 10)) > -1) {
  51.             return "https://api.tiktokv.com/aweme/v1/play/" + value.substring(i, e) + "&improve_bitrate=1&ratio=default";
  52.         }
  53.         return value;
  54.     }
  55.  
  56.     function generateDeviceId() {
  57.         var i = 1, a = new Array(19); //Math.floor(Math.random() * 9) + 11
  58.         a[0] = 54; //Math.floor(Math.random() * 9) + 49
  59.         while (i < a.length) {
  60.             a[i++] = Math.floor(Math.random() * 10) + 48;
  61.         }
  62.         return String.fromCharCode.apply(null, a);
  63.     }
  64.  
  65.     function getAPIData(ID, q, btn1, btn2) {
  66.         const d = new Date();
  67.         const t = d.getTime() - d.getTimezoneOffset() * 60000;
  68.         const deviceVersion = generateBase62(8);
  69.         const deviceId = generateDeviceId();
  70.         const iId = generateDeviceId();
  71.         const url = "https://api-t2.tiktokv.com/aweme/v1/multi/aweme/detail/?aweme_ids="
  72.         + "[" + ID + "]"
  73.         + "&region=US"
  74.         + "&ts=" + Math.floor(t / 1000)
  75.         + "&timezone_name=Etc%2FGMT"
  76.         + "&device_type=Pixel%20" + deviceVersion
  77.         + "&iid=" + iId
  78.         + "&locale=en"
  79.         + "&app_type=normal"
  80.         + "&resolution=1080*1920"
  81.         + "&aid=1180"
  82.         + "&app_name=musical_ly"
  83.         + "&_rticket=" + t
  84.         + "&device_platform=android"
  85.         + "&version_code=100000"
  86.         + "&dpi=441"
  87.         + "&cpu_support64=false"
  88.         + "&sys_region=US"
  89.         + "&timezone_offset=0"
  90.         + "&device_id=" + deviceId
  91.         + "&pass-route=1"
  92.         + "&device_brand=google"
  93.         + "&os_version=8.0.0"
  94.         + "&op_region=US"
  95.         + "&app_language=en"
  96.         + "&pass-region=1"
  97.         + "&language=en"
  98.         + "&channel=googleplay"
  99.  
  100.  
  101.         GM_xmlhttpRequest({
  102.             method:     "GET",
  103.             url:        url,
  104.             headers:    {
  105.                 "User-Agent": "okhttp"
  106.             },
  107.             onload:  function(details) {
  108.                 downloadLink(details, q, btn1, btn2);
  109.             }
  110.         })
  111.     }
  112.  
  113.     function downloadLink(dat, q, btn1, btn2) {
  114.         if (!btn1 || !btn2) return;
  115.  
  116.         try {
  117.             dat = JSON.parse(dat.responseText);
  118.             const ID = dat.aweme_details[0].aweme_id
  119.             const DESC = dat.aweme_details[0].desc
  120.  
  121.             dat = dat.aweme_details[0].video.play_addr.url_list;
  122.             if (q){
  123.                 dat = getQualityUri(dat[dat.length - 1]);
  124.             } else {
  125.                 dat = dat[dat.length - 1]
  126.                     .replace("improve_bitrate=0", "improve_bitrate=1")
  127.                     .replace(/ratio=\d+p/i, "ratio=default");
  128.             }
  129.  
  130.             const fileName = DESC ? `${ID} ${DESC}.mp4` : `${ID}.mp4`;
  131.             GM_download({
  132.                 url: dat,
  133.                 name: fileName,
  134.                 headers:    {
  135.                     "User-Agent": "okhttp"
  136.                 },
  137.                 onload: setTimeout(() => { btn1.disabled = !1; btn2.disabled = !1; }, 3000)
  138.             });
  139.  
  140.         }
  141.         catch(e) { console.log(e); return; }
  142.     }
  143.  
  144.     function createTwoButtons(vidId, className1, className2){
  145.         const downloadBtn = document.createElement('button');
  146.         downloadBtn.innerHTML = '▼';
  147.         downloadBtn.className = className1;
  148.  
  149.         const downloadQualityBtn = document.createElement('button');
  150.         downloadQualityBtn.innerHTML = '▼ <b>HD</b>';
  151.         downloadQualityBtn.className = className2;
  152.  
  153.         downloadBtn.onclick = () => {
  154.             downloadBtn.disabled = !0;
  155.             downloadQualityBtn.disabled = !0;
  156.             getAPIData(vidId, false, downloadBtn, downloadQualityBtn);
  157.         }
  158.  
  159.         downloadQualityBtn.onclick = () => {
  160.             downloadBtn.disabled = !0;
  161.             downloadQualityBtn.disabled = !0;
  162.             getAPIData(vidId, true, downloadBtn, downloadQualityBtn);
  163.         }
  164.         return [downloadBtn, downloadQualityBtn]
  165.     }
  166.  
  167.     function addDownloadButtons(videoCardTag) {
  168.         if (document.querySelector('.card-button:not(.feed-button)')) return;
  169.  
  170.         const vidId = document.location.href.match(/video\/([6789]\d+)/i)[1];
  171.         var className1 = 'card-button';
  172.         var className2 = 'card-hires-button';
  173.         if (document.querySelector(".trending-title")) {
  174.             className1 += ' vid_height_add';
  175.             className2 += ' vid_height_add';
  176.         }
  177.  
  178.         var buttons = createTwoButtons(vidId, className1, className2);
  179.  
  180.         videoCardTag
  181.             .parentNode
  182.             .appendChild(buttons[0])
  183.             .parentNode
  184.             .appendChild(buttons[1]);
  185.     }
  186.  
  187.      function addFeedDownloadButtons(videoCardTag) {
  188.         const vidId = videoCardTag.parentElement.querySelector('a').href.match(/video\/([6789]\d+)/i)[1];
  189.         const className1 = 'feed-button card-button';
  190.         const className2 = 'feed-hires-button card-hires-button';
  191.  
  192.         var buttons = createTwoButtons(vidId, className1, className2);
  193.  
  194.         videoCardTag.classList.add('bFinalized');
  195.         videoCardTag
  196.             .appendChild(buttons[0])
  197.             .parentNode
  198.             .appendChild(buttons[1]);
  199.  
  200.      }
  201.  
  202.     const bodyObserver = new MutationObserver(() => {
  203.         const card = document.querySelector('.video-card-browse > video');
  204.         const feedCards = document.querySelectorAll('.pc-action-bar:not(.bFinalized)');
  205.         card && addDownloadButtons(card);
  206.         feedCards.length && feedCards.forEach((c) => {
  207.             c && addFeedDownloadButtons(c);
  208.         })
  209.     });
  210.  
  211.     bodyObserver.observe(document.body, {
  212.         childList: !0,
  213.         subtree: !0,
  214.         attributes: !1
  215.     });
  216. })();
RAW Paste Data