-
Notifications
You must be signed in to change notification settings - Fork 125
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1efe9e0
commit 6fec6dd
Showing
2 changed files
with
117 additions
and
152 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,164 +1,36 @@ | ||
let express = require('express'); | ||
const axios = require('axios'); | ||
const res = require('express/lib/response'); | ||
const errwin = /[\\\n\r/:*?\"<>|]/g; | ||
const subwin = ``; | ||
const httpurl = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/ | ||
// 获取XB参数 | ||
const express = require('express'); | ||
const { GetID, GetInfo } = require('../utils/douyinService'); | ||
const getXB = require('../utils/x-bogus.js') | ||
// 单作品接口 | ||
const aweme_url = 'https://www.douyin.com/aweme/v1/web/aweme/detail/?' | ||
|
||
let router = express.Router(); | ||
|
||
// 获取作品ID | ||
var GetID = async function (res,dyurl) { | ||
/* 获取API */ | ||
router.get('/', async function(req, res, next) { | ||
try { | ||
return await new Promise((resolve, reject) => { | ||
try { | ||
axios.get(dyurl, { | ||
headers: { | ||
'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1' | ||
} | ||
}).then(function (response) { | ||
// console.log(response.request.res.responseUrl); | ||
var revideo = /video\/(\d*)/; | ||
var item_ids = revideo.exec(response.request.res.responseUrl)[1]; | ||
console.log('作品id ' + item_ids); | ||
resolve(item_ids); | ||
}).catch(function (error) { | ||
console.log(error + ' item_ids获取错误'); | ||
res.render('error'); | ||
reject(error); | ||
}); | ||
} catch (error_1) { | ||
console.log('获取作品ID失败 ' + error_1); | ||
} | ||
}); | ||
} catch (error_2) { | ||
console.log(error_2); | ||
res.render('error'); | ||
reject(error_2); | ||
} | ||
} | ||
// 尝试从cookies中获取并解析dycookie | ||
let dyCookie; | ||
try { | ||
dyCookie = JSON.parse(req.cookies['dycookie']); | ||
} catch { | ||
dyCookie = null; | ||
} | ||
|
||
// 获取作品信息 | ||
var GetInfo = async function (res,item_ids,dycookie) { | ||
try { | ||
return await new Promise((resolve, reject) => { | ||
const params_url = `aweme_id=${item_ids}&aid=1128&version_name=23.5.0&device_platform=android&os_version=2333`; | ||
let xb = getXB(params_url); | ||
console.log(aweme_url + params_url + `&X-Bogus=${xb}`); | ||
axios.get(aweme_url + params_url + `&X-Bogus=${xb}`, { | ||
headers: { | ||
'cookie': `odin_tt=${dycookie['odin_tt']};sessionid_ss=${dycookie['sessionid_ss']};ttwid=${dycookie['ttwid']};passport_csrf_token=${dycookie['passport_csrf_token']};msToken=${dycookie['msToken']};`, | ||
'referer': 'https://www.douyin.com/', | ||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36' | ||
} | ||
}) | ||
.then(function (response) { | ||
console.log('GetInfo ok'); | ||
let { | ||
status_code | ||
} = response.data; | ||
// console.log(response) | ||
if (status_code === 0) { | ||
// 无水印视频链接 | ||
// let url = item_list.video.play_addr.url_list[0].replace( | ||
// 'playwm', | ||
// 'play' | ||
// ) | ||
// let url = response.data.aweme_detail.video.play_addr.url_list[2] | ||
let uri = response.data.aweme_detail.video.play_addr.uri; | ||
let music = response.data.aweme_detail.music.play_url.uri; | ||
let m_title = response.data.aweme_detail.music.title; | ||
let unique_id = response.data.aweme_detail.author.unique_id; | ||
let video_id = response.data.aweme_detail.aweme_id; | ||
let aweme_type = response.data.aweme_detail.aweme_type; | ||
let nickname = response.data.aweme_detail.author.nickname; | ||
let userhome = 'https://www.douyin.com/user/' + response.data.aweme_detail.author.sec_uid; | ||
let newimages = Array(); | ||
// 没有设置抖音号则获取短号 | ||
if (unique_id == '') { | ||
unique_id = response.data.aweme_detail.author.short_id; | ||
} | ||
if (aweme_type == '0') { | ||
var type = '视频'; | ||
var images = ''; | ||
} else { | ||
var type = '图集'; | ||
var images = response.data.aweme_detail.images; | ||
for (var i in images) { | ||
newimages.push(images[i].url_list[0]); | ||
} | ||
} | ||
// 转换成1080p | ||
url = `http://aweme.snssdk.com/aweme/v1/play/?video_id=${uri}&ratio=1080p`; | ||
// 视频文案过滤非法字符 | ||
let desc = response.data.aweme_detail.desc.replaceAll(errwin, subwin); | ||
|
||
console.log('video play url ', url); | ||
console.log('video desc ', desc); | ||
var data = ({ | ||
url: url, | ||
desc: desc, | ||
music: music, | ||
m_title: m_title, | ||
nickname: nickname, | ||
unique_id: unique_id, | ||
video_id: video_id, | ||
userhome: userhome, | ||
type: type, | ||
images: newimages | ||
}); | ||
resolve(data); | ||
} else { | ||
console.log(status_code); | ||
reject(status_code); | ||
} | ||
}).catch(function (error) { | ||
console.log(error); | ||
res.render('error'); | ||
reject(error); | ||
}); | ||
}); | ||
} catch (error_1) { | ||
console.log(error_1); | ||
res.render('error'); | ||
reject(error_1); | ||
} | ||
} | ||
// 如果未提供URL,则使用默认视频 | ||
let videoUrl = req.query.url; | ||
if (!videoUrl) { | ||
videoUrl = 'https://v.douyin.com/NKyY6Ch/'; | ||
} | ||
|
||
/* GET api. */ | ||
router.get('/', async function(req, res, next) { | ||
console.log(req.cookies['dycookie']) | ||
if(req.cookies['dycookie'] == undefined){ | ||
var data = ({ | ||
work:false | ||
}) | ||
res.render('index', data); | ||
return | ||
} | ||
if (req.query.url == ''){ | ||
// 默认视频 | ||
req.query.url = 'https://v.douyin.com/NKyY6Ch/' | ||
} | ||
console.log('open shorturl ok'); | ||
// console.log(req.query.url) | ||
|
||
// let urlReg = /(http:\/\/|https:\/\/)((\w|=|\?|\.|\/|&|-)+)/g | ||
// let dyurl = urlReg.exec(req.query.url)[0] | ||
let dyurl = req.query.url; | ||
await GetID(res,dyurl).then(item_ids => { | ||
GetInfo(res,item_ids,req.cookies['dycookie']).then(data => { | ||
//console.log('data',data); | ||
res.render('index', { data }); | ||
}); | ||
}).catch((error) =>{ | ||
console.log('GetID Error',error); | ||
res.render('error'); | ||
}); | ||
// 使用视频ID和cookie获取视频详情 | ||
const videoData = await GetInfo(videoId, dyCookie, getXB); | ||
res.render('index', { videoData }); | ||
} catch (error) { | ||
next(error); // 转发错误到错误处理中间件 | ||
} | ||
}); | ||
|
||
console.log('open url ok'); | ||
}); | ||
|
||
module.exports = router; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
const axios = require("axios"); | ||
|
||
// 常量定义 | ||
const invalid = /[\\\n\r/:*?\"<>|]/g; | ||
const repWith = ``; | ||
const AWE_URL_BASE = "http://aweme.snssdk.com/aweme/v1/play/?"; | ||
const DETAIL_URL_BASE = 'https://www.douyin.com/aweme/v1/web/aweme/detail/?' | ||
const USER_AGENT_MOBILE = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"; | ||
const USER_AGENT_DESKTOP = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1"; | ||
const VIDEO_REGEX = /video\/(\d*)/; | ||
const NOTE_REGEX = /note\/(\d*)/; | ||
|
||
/** | ||
* 获取作品ID | ||
* @param {string} dyurl - 抖音短链接。 | ||
* @returns {string} 作品ID | ||
* @throws {Error} 在请求失败或解析ID时可能会抛出错误。 | ||
*/ | ||
async function GetID(dyurl) { | ||
const response = await axios.get(dyurl, { | ||
headers: { "user-agent": USER_AGENT_DESKTOP }, | ||
}); | ||
|
||
if (response.request.res.responseUrl.includes('video')) { | ||
item_ids = VIDEO_REGEX.exec(response.request.res.responseUrl)[1]; | ||
} else if (response.request.res.responseUrl.includes('note')) { | ||
item_ids = NOTE_REGEX.exec(response.request.res.responseUrl)[1]; | ||
} else { | ||
console.error("URL格式不匹配任何已知模式"); | ||
return; | ||
} | ||
return item_ids; | ||
} | ||
|
||
/** | ||
* 根据作品ID和cookie获取作品详细信息 | ||
* @param {string} item_ids - 作品ID。 | ||
* @param {Object} dycookie - 抖音cookie对象。 | ||
* @param {Function} getXB - 获取XB参数的函数。 | ||
* @returns {Object} 作品的详细信息 | ||
* @throws {Error} 在请求失败或解析数据时可能会抛出错误。 | ||
*/ | ||
async function GetInfo(item_ids, dycookie, getXB) { | ||
// 构造请求URL | ||
const params_url = `aweme_id=${item_ids}&aid=1128&version_name=23.5.0&device_platform=android&os_version=2333`; | ||
const xb = getXB(params_url); | ||
const url = `${DETAIL_URL_BASE}${params_url}&X-Bogus=${xb}`; | ||
|
||
const response = await axios.get(url, { | ||
headers: { | ||
'cookie': `odin_tt=${dycookie["odin_tt"]};sessionid_ss=${dycookie["sessionid_ss"]};ttwid=${dycookie['ttwid']};passport_csrf_token=${dycookie['passport_csrf_token']};msToken=${dycookie['msToken']};`, | ||
'referer': 'https://www.douyin.com/', | ||
'user-agent': USER_AGENT_MOBILE | ||
} | ||
}); | ||
|
||
// 如果response.data为空或未定义 | ||
if (!response.data) { | ||
return { work: false }; | ||
} | ||
|
||
// 校验响应状态 | ||
if (response.data.status_code === 0) { | ||
// 提取需要的数据 | ||
const { video, music, author, desc, aweme_id, aweme_type } = response.data.aweme_detail; | ||
const unique_id = author.unique_id || author.short_id; // 如果unique_id为空,则使用short_id | ||
const userhome = `https://www.douyin.com/user/${author.sec_uid}`; | ||
const type = Number(aweme_type) === 0 ? '视频' : '图集'; | ||
const images = Number(aweme_type) !== 0 ? response.data.aweme_detail.images.map(image => image.url_list[0]) : []; | ||
//const images = aweme_type !== 0 && response.data.aweme_detail.images ? response.data.aweme_detail.images.map(image => image.url_list[0]) : []; | ||
|
||
const url = video?.bit_rate?.[0]?.play_addr?.url_list?.[0] ?? ''; | ||
const cleanedDesc = desc.replaceAll(invalid, repWith); | ||
|
||
return { | ||
url, | ||
desc: cleanedDesc, | ||
music: music.play_url.uri, | ||
m_title: music.title, | ||
nickname: author.nickname, | ||
unique_id, | ||
video_id: aweme_id, | ||
userhome, | ||
type, | ||
images | ||
}; | ||
} else { | ||
// 如果响应状态码不为0,抛出错误 | ||
throw new Error(`Error with status code: ${response.data.status_code}`); | ||
} | ||
} | ||
|
||
module.exports = { GetID, GetInfo }; |