您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Download videos from Douyin website
- // ==UserScript==
- // @name [J]Douyin Video Downloader
- // @namespace http://tampermonkey.net/
- // @version 2024120821
- // @description Download videos from Douyin website
- // @author jeffc
- // @match *.douyin.com/*
- // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
- // @grant none
- // @license MIT
- // ==/UserScript==
- (function() {
- 'use strict';
- function Video(authorName, desc, id) {
- this.authorName = authorName || "";
- this.videoDesc = desc || "";
- this.videoId = id || "";
- this.videoUrl = "";
- }
- Video.prototype = {
- constructor: Video,
- download: function() {
- var xhr = new XMLHttpRequest();
- xhr.open('GET', `https://www.douyin.com/aweme/v1/web/aweme/detail/?device_platform=webapp&aweme_id=${this.videoId}&screen_width=1920&screen_height=1080`, true);
- xhr.responseType = 'json';
- xhr.onload = () => {
- if (xhr.status === 200) {
- var responseData = xhr.response;
- this.videoUrl = responseData.aweme_detail.video.bit_rate[0].play_addr.url_list[0];
- this.save();
- } else {
- console.error('request error:', xhr.statusText);
- }
- };
- xhr.onerror = function() {
- console.error('request error:', xhr.statusText);
- };
- xhr.send();
- },
- clear: function() {
- this.authorName = "";
- this.videoDesc = "";
- this.videoId = "";
- this.videoUrl = "";
- },
- save: function() {
- if (this.videoUrl.length > 0) {
- let downloadDom = document.createElement('a');
- downloadDom.href = this.videoUrl;
- downloadDom.target = "_blank";
- // downloadDom.download = `${this.authorName}_${this.videoDesc}.mp4`;
- document.body.appendChild(downloadDom);
- downloadDom.setAttribute("download",((new Date()).getTime())+".mp4")
- downloadDom.click();
- document.body.removeChild(downloadDom);
- this.clear();
- } else {
- alert("无法解析视频");
- }
- }
- };
- const _historyWrap = function(type) {
- const orig = history[type];
- const e = new Event(type);
- return function() {
- const rv = orig.apply(this, arguments);
- e.arguments = arguments;
- window.dispatchEvent(e);
- return rv;
- };
- }
- history.pushState = _historyWrap('pushState');
- history.replaceState = _historyWrap('replaceState');
- window.addEventListener('pushState', function(e) {
- console.log('page change ');
- dynamicMonitoring();
- });
- window.addEventListener('replaceState', function(e) {
- console.log('page change ');
- dynamicMonitoring();
- });
- const PageType = {
- Detail: 'Detail', // 详情页
- Normal: 'Normal', // 作者主页
- Live: 'Live', // 直播
- };
- let currentPageType = PageType.Normal;
- let liveExecuStatus = false;
- // 动态监测函数
- function dynamicMonitoring() {
- if (/www.douyin.com\/video\/[0-9]{9,}/.test(window.location.href)) {
- currentPageType = PageType.Detail;
- var initialTargetNode = document.querySelector('xg-video-container');
- var mobserver = new MutationObserver(function(mutationsList, observer) {
- mutationsList.forEach(function(mutation) {
- refreshDownloadDom(initialTargetNode);
- });
- });
- var mconfig = {
- childList:true,
- subtree:true
- };
- let listener = setInterval(function() {
- if (!initialTargetNode ) {
- initialTargetNode = document.querySelector('xg-video-container');
- }else{
- mobserver.observe(initialTargetNode, mconfig);
- refreshDownloadDom(initialTargetNode);
- clearInterval(listener);
- }
- }, 500);
- } else if(!liveExecuStatus && ( /live.douyin.com\/[0-9]{9,}/.test(window.location.href) || /www.douyin.com\/follow\/live\/[0-9]{9,}/.test(window.location.href))){
- liveExecuStatus = true;
- var targetUrl="";
- const originalFetch = window.fetch;
- window.fetch = function(url, options) {
- if (url.includes('.mp4') || url.includes('.m3u8') || url.includes('.ts') || url.includes('.flv')) {
- targetUrl = url;
- }
- return originalFetch.apply(this, [url, options]);
- };
- var findlive = setInterval(function(){
- if(targetUrl != "" && document.querySelector(".__leftContainer"))
- {
- console.log("=============");
- addcss();
- var savebtn = document.createElement("button");
- savebtn.textContent="直播录制";
- savebtn.className="jbtn";
- savebtn.addEventListener("click",function(){
- var ddd = document.createElement('a');
- ddd.href = targetUrl;
- ddd.target = "_blank";
- ddd.setAttribute("download","");
- document.body.appendChild(ddd);
- ddd.click();
- document.body.removeChild(ddd);
- });
- document.querySelector(".__leftContainer").appendChild(savebtn);
- clearInterval(findlive);
- savebtn.previousSibling.style="margin-right:10px";
- }
- },200);
- } else {
- // 通用页
- currentPageType = PageType.Normal;
- monitoringSilder();
- var observeTargetNode;
- var observer = new IntersectionObserver(function(entries) {
- entries.forEach(function(entry) {
- switchObserverTarget();
- });
- });
- var config = {
- attributes: true,
- attributeFilter: ['data-e2e']
- };
- let listener = setInterval(function() {
- if (!observeTargetNode ) {
- observeTargetNode = document.querySelector('div[data-e2e="feed-active-video"]');
- }else{
- observer.observe(observeTargetNode, config);
- refreshDownloadDom(observeTargetNode);
- clearInterval(listener);
- }
- }, 500);
- // 切换观察目标节点的函数
- function switchObserverTarget() {
- // 重新获取目标节点
- var newTargetNode = document.querySelector('div[data-e2e="feed-active-video"]');
- let listener = setInterval(function() {
- if (!newTargetNode ) {
- newTargetNode = document.querySelector('div[data-e2e="feed-active-video"]');
- }else{
- if (newTargetNode && newTargetNode !== observeTargetNode) {
- observer.unobserve(observeTargetNode);
- observer.observe(newTargetNode, config);
- observeTargetNode = newTargetNode;
- refreshDownloadDom(newTargetNode);
- }
- clearInterval(listener);
- }
- }, 500);
- }
- }
- }
- // 更新通用页的下载按钮
- function refreshDownloadDom(activeVideoDom) {
- var downloadBtn = activeVideoDom.querySelector('#douyin_download_by_jeffc');
- if (!downloadBtn) {
- downloadBtn = document.createElement('div');
- downloadBtn.setAttribute("data-index", "10");
- downloadBtn.id = "douyin_download_by_jeffc";
- downloadBtn.innerHTML = "DOWNLOAD";
- downloadBtn.style = 'cursor:pointer;width: 60px;text-align: center;font-size: 14px;color: rgba(255, 255, 255,0.75);line-height:20px;margin-right: 30px;';
- downloadBtn.addEventListener('click', function() {
- downloadVideo(activeVideoDom);
- });
- let listenerActiveVideo = setInterval(function() {
- let targetNode = currentPageType == currentPageType.Normal ? activeVideoDom.querySelector("xg-right-grid") : activeVideoDom.parentNode.querySelector("xg-right-grid") ;
- if (targetNode ) {
- if(!targetNode.querySelector("#douyin_download_by_jeffc"))
- {
- targetNode.appendChild(downloadBtn);
- }
- clearInterval(listenerActiveVideo);
- }
- }, 500);
- }
- }
- // 下载视频的函数
- function downloadVideo(activeVideoDom) {
- var authorName = activeVideoDom.querySelector('[data-e2e="feed-video-nickname"]')?.innerText.replace(/[^\u4e00-\u9fa5a-zA-Z0-9\s]/g, '');
- var videoDesc = activeVideoDom.querySelector('[data-e2e="video-desc"]')?.innerText.replace(/[^\u4e00-\u9fa5a-zA-Z0-9\s]/g, '');
- var videoDom = activeVideoDom.querySelector("video");
- var sourceUrl;
- if (videoDom) {
- if (videoDom.childNodes.length > 0) {
- sourceUrl = videoDom.querySelector("source")?.src;
- }
- } else {
- sourceUrl = activeVideoDom.querySelector("xg-video")?.src;
- }
- if (sourceUrl) {
- // 直接下载
- var downloadDom = document.createElement('a');
- downloadDom.href = sourceUrl;
- downloadDom.target = "_blank";
- downloadDom.setAttribute("download","");
- document.body.appendChild(downloadDom);
- downloadDom.click();
- document.body.removeChild(downloadDom);
- } else {
- // 解析视频并下载
- var vid = activeVideoDom.getAttribute("data-e2e-vid");
- (new Video(authorName, videoDesc, vid)).download();
- }
- }
- function addcss()
- {
- const style = document.createElement('style');
- style.innerHTML = `
- .jbtn {
- magin-left:20px;
- padding: 5px 25px;
- font-size: 14px;
- border: none;
- outline: none;
- color: rgb(255, 255, 255);
- background: #111;
- cursor: pointer;
- position: relative;
- z-index: 0;
- border-radius: 10px;
- user-select: none;
- -webkit-user-select: none;
- touch-action: manipulation;
- }
- .jbtn:before {
- content: "";
- background: linear-gradient(
- 45deg,
- #ff0000,
- #ff7300,
- #fffb00,
- #48ff00,
- #00ffd5,
- #002bff,
- #7a00ff,
- #ff00c8,
- #ff0000
- );
- position: absolute;
- top: -2px;
- left: -2px;
- background-size: 400%;
- z-index: -1;
- filter: blur(5px);
- -webkit-filter: blur(5px);
- width: calc(100% + 4px);
- height: calc(100% + 4px);
- animation: glowing-jbtn 20s linear infinite;
- transition: opacity 0.3s ease-in-out;
- border-radius: 10px;
- }
- @keyframes glowing-jbtn {
- 0% {
- background-position: 0 0;
- }
- 50% {
- background-position: 400% 0;
- }
- 100% {
- background-position: 0 0;
- }
- }
- .jbtn:after {
- z-index: -1;
- content: "";
- position: absolute;
- width: 100%;
- height: 100%;
- background: #222;
- left: 0;
- top: 0;
- border-radius: 10px;
- }
- `;
- document.head.appendChild(style);
- }
- function monitoringSilder()
- {
- const mutationObserver = new MutationObserver(mutationsList => {
- mutationsList.forEach(mutation => {
- if (mutation.type === 'attributes' && mutation.attributeName === 'data-e2e-vid') {
- const targetNode = mutation.target;
- console.log(targetNode);
- dealwith(targetNode);
- }
- });
- });
- let listener = setInterval(function() {
- if (document.querySelector('#slidelist')) {
- mutationObserver.observe(document.querySelector('#slidelist'), { childList: true, subtree: true,attributes:true});
- clearInterval(listener);
- }
- }, 500);
- function dealwith(activeVideoDom){
- var downloadBtn = activeVideoDom.querySelector('#douyin_download_by_jeffc');
- if (!downloadBtn) {
- downloadBtn = document.createElement('div');
- downloadBtn.setAttribute("data-index", "10");
- downloadBtn.id = "douyin_download_by_jeffc";
- downloadBtn.innerHTML = "DOWNLOAD";
- downloadBtn.style = 'cursor:pointer;width: 60px;text-align: center;font-size: 14px;color: rgba(255, 255, 255,0.75);line-height:20px;margin-right: 30px;';
- downloadBtn.addEventListener('click', function() {
- downloadVideo(activeVideoDom);
- });
- let listenerActiveVideo = setInterval(function() {
- let targetNode = currentPageType == currentPageType.Normal ? activeVideoDom.querySelector("xg-right-grid") : activeVideoDom.parentNode.querySelector("xg-right-grid") ;
- if (targetNode ) {
- if(!targetNode.querySelector("#douyin_download_by_jeffc"))
- {
- targetNode.appendChild(downloadBtn);
- }
- clearInterval(listenerActiveVideo);
- }
- }, 500);
- }
- }
- }
- // 页面加载完成后开始动态监测
- window.onload = function() {
- dynamicMonitoring();
- };
- // 监听键盘按下事件
- document.addEventListener("keydown", function(event) {
- // 获取按下的键的键码
- var keyCode = event.keyCode;
- if(keyCode == 90)
- {
- var target_dom = document.querySelectorAll("#douyin_download_by_jeffc");
- if(target_dom)
- {
- target_dom[target_dom.length-1].click();
- }
- }
- });
- })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址