mirror of
https://github.com/wukko/cobalt.git
synced 2025-05-03 15:44:27 +02:00
135 lines
3.8 KiB
JavaScript
135 lines
3.8 KiB
JavaScript
import Cookie from "../cookie/cookie.js";
|
|
|
|
import { extract } from "../url.js";
|
|
import { genericUserAgent } from "../../config.js";
|
|
import { updateCookie } from "../cookie/manager.js";
|
|
import { createStream } from "../../stream/manage.js";
|
|
|
|
const shortDomain = "https://vt.tiktok.com/";
|
|
|
|
export default async function(obj) {
|
|
const cookie = new Cookie({});
|
|
let postId = obj.postId;
|
|
|
|
if (!postId) {
|
|
let html = await fetch(`${shortDomain}${obj.shortLink}`, {
|
|
redirect: "manual",
|
|
headers: {
|
|
"user-agent": genericUserAgent.split(' Chrome/1')[0]
|
|
}
|
|
}).then(r => r.text()).catch(() => {});
|
|
|
|
if (!html) return { error: "fetch.fail" };
|
|
|
|
if (html.startsWith('<a href="https://')) {
|
|
const extractedURL = html.split('<a href="')[1].split('?')[0];
|
|
const { patternMatch } = extract(extractedURL);
|
|
postId = patternMatch.postId;
|
|
}
|
|
}
|
|
if (!postId) return { error: "fetch.short_link" };
|
|
|
|
// should always be /video/, even for photos
|
|
const res = await fetch(`https://tiktok.com/@i/video/${postId}`, {
|
|
headers: {
|
|
"user-agent": genericUserAgent,
|
|
cookie,
|
|
}
|
|
})
|
|
updateCookie(cookie, res.headers);
|
|
|
|
const html = await res.text();
|
|
|
|
let detail;
|
|
try {
|
|
const json = html
|
|
.split('<script id="__UNIVERSAL_DATA_FOR_REHYDRATION__" type="application/json">')[1]
|
|
.split('</script>')[0]
|
|
const data = JSON.parse(json)
|
|
detail = data["__DEFAULT_SCOPE__"]["webapp.video-detail"]["itemInfo"]["itemStruct"]
|
|
} catch {
|
|
return { error: "fetch.fail" };
|
|
}
|
|
|
|
let video, videoFilename, audioFilename, audio, images,
|
|
filenameBase = `tiktok_${detail.author.uniqueId}_${postId}`,
|
|
bestAudio; // will get defaulted to m4a later on in match-action
|
|
|
|
images = detail.imagePost?.images;
|
|
|
|
let playAddr = detail.video.playAddr;
|
|
if (obj.h265) {
|
|
const h265PlayAddr = detail?.video?.bitrateInfo?.find(b => b.CodecType.includes("h265"))?.PlayAddr.UrlList[0]
|
|
playAddr = h265PlayAddr || playAddr
|
|
}
|
|
|
|
if (!obj.isAudioOnly && !images) {
|
|
video = playAddr;
|
|
videoFilename = `${filenameBase}.mp4`;
|
|
} else {
|
|
audio = playAddr;
|
|
audioFilename = `${filenameBase}_audio`;
|
|
|
|
if (obj.fullAudio || !audio) {
|
|
audio = detail.music.playUrl;
|
|
audioFilename += `_original`
|
|
}
|
|
if (audio.includes("mime_type=audio_mpeg")) bestAudio = 'mp3';
|
|
}
|
|
|
|
if (video) {
|
|
return {
|
|
urls: video,
|
|
filename: videoFilename,
|
|
headers: { cookie }
|
|
}
|
|
}
|
|
|
|
if (images && obj.isAudioOnly) {
|
|
return {
|
|
urls: audio,
|
|
audioFilename: audioFilename,
|
|
isAudioOnly: true,
|
|
bestAudio,
|
|
headers: { cookie }
|
|
}
|
|
}
|
|
|
|
if (images) {
|
|
let imageLinks = images
|
|
.map(i => i.imageURL.urlList.find(p => p.includes(".jpeg?")))
|
|
.map((url, i) => {
|
|
if (obj.alwaysProxy) url = createStream({
|
|
service: "tiktok",
|
|
type: "proxy",
|
|
url,
|
|
filename: `${filenameBase}_photo_${i + 1}.jpg`
|
|
})
|
|
|
|
return {
|
|
type: "photo",
|
|
url
|
|
}
|
|
});
|
|
|
|
return {
|
|
picker: imageLinks,
|
|
urls: audio,
|
|
audioFilename: audioFilename,
|
|
isAudioOnly: true,
|
|
bestAudio,
|
|
headers: { cookie }
|
|
}
|
|
}
|
|
|
|
if (audio) {
|
|
return {
|
|
urls: audio,
|
|
audioFilename: audioFilename,
|
|
isAudioOnly: true,
|
|
bestAudio,
|
|
headers: { cookie }
|
|
}
|
|
}
|
|
}
|