├── .gitignore ├── readme.md ├── tool.js ├── package.json ├── index.js └── core.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | .vscode 4 | .eslintcache 5 | dist 6 | logs 7 | var 8 | node_modules/ 9 | npm-debug.log* 10 | config.json 11 | package-lock.json 12 | .csscomb.json 13 | .cache-loader 14 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # dy 去水印 node.js(仅供学习使用) 2 | 3 | ### 浏览器访问模式 4 | ```bash 5 | npm start 6 | ``` 7 | 8 | 然后打开 http://localhost:3000/dy?url=输入从抖音复制的url地址 9 | 10 | ### 工具模式 11 | 12 | ```bash 13 | node tool.js //会把无水印视频下载到本地 14 | ``` 15 | ### 原理说明 16 | [文章详解](https://juejin.im/post/5ee4f035e51d4578615af59f) 17 | 18 | 19 | -------------------------------------------------------------------------------- /tool.js: -------------------------------------------------------------------------------- 1 | const { runDouyin } = require('./core'); 2 | const fs = require('fs'); 3 | 4 | async function tool() { 5 | const { videoStream, share_title } = await runDouyin( 6 | 'https://v.douyin.com/JdngHhh/' 7 | ); 8 | videoStream.pipe(fs.createWriteStream(`${share_title}(无水印).mp4`)); // 下载到本地 9 | } 10 | 11 | tool(); 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "douyin", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js" 8 | }, 9 | "keywords": [], 10 | "author": "Faithree", 11 | "license": "ISC", 12 | "dependencies": { 13 | "axios": "^0.19.2", 14 | "express": "^4.17.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { runDouyin } = require('./core'); 3 | // const fs = require('fs'); 4 | const app = express(); 5 | const port = 3000; 6 | app.get('/dy', async (req, res) => { 7 | if (req.query.url) { 8 | try { 9 | const { videoStream, share_title } = await runDouyin(req.query.url); 10 | res.attachment(`${share_title}(无水印).mp4`); 11 | videoStream.pipe(res); 12 | } catch (e) { 13 | console.log(e); 14 | res.send('错误: ' + e); 15 | } 16 | } else { 17 | res.send('url错误'); 18 | } 19 | }); 20 | 21 | app.listen(port, () => console.log(`Example app listening on port ${port}!`)); 22 | -------------------------------------------------------------------------------- /core.js: -------------------------------------------------------------------------------- 1 | // import axios from 'axios'; 2 | const axios = require('axios'); 3 | const userAgent = 4 | 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Mobile Safari/537.36'; 5 | async function request(url, type) { 6 | const option = { 7 | url, 8 | method: 'get', 9 | headers: { 10 | 'user-agent': userAgent, 11 | }, 12 | }; 13 | if (type) { 14 | option.responseType = type; 15 | } 16 | return axios(option); 17 | } 18 | 19 | async function runDouyin(shareUrl) { 20 | // 1.根据分享的视频地址,通过重定向获取整个html信息 21 | const { data: html } = await request(shareUrl); 22 | // 2.截取itemId, dytk 发起二次请求获取uriId 23 | const itemId = html.match(/(?<=itemId:\s\")\d+(?=\")/g)[0]; 24 | const dytk = html.match(/(?<=dytk:\s\")(.*?)(?=\")/g)[0]; 25 | const long_url = `https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=${itemId}&dytk=${dytk}`; 26 | const { data: videoJson } = await request(long_url); 27 | // 3.最后通过uri参数来调用视频下载接口 28 | const uriId = videoJson.item_list[0].video.play_addr.uri; 29 | const share_title = videoJson.item_list[0].share_info.share_title; 30 | const noWatermarkUrl = `https://aweme.snssdk.com/aweme/v1/play/?video_id=${uriId}&line=0&ratio=540p&media_type=4&vr_type=0&improve_bitrate=0&is_play_url=1&is_support_h265=0&source=PackSourceEnum_PUBLISH`; 31 | const { data: videoStream } = await request(noWatermarkUrl, 'stream'); 32 | return { videoStream, share_title }; 33 | } 34 | 35 | module.exports = { 36 | runDouyin, 37 | }; 38 | --------------------------------------------------------------------------------