fix tiktok downloads (#407)

This commit is contained in:
wukko 2024-03-29 06:57:05 +06:00 committed by GitHub
commit 36c697cc5f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 57 additions and 51 deletions

View file

@ -59,9 +59,32 @@ sudo service nscd start
| `CORS_URL` | not used | `https://cobalt.tools/` | cross-origin resource sharing url. api will be available only from this url if `CORS_WILDCARD` is set to `0`. |
| `COOKIE_PATH` | not used | `/cookies.json` | path for cookie file relative to main folder. |
| `PROCESSING_PRIORITY` | not used | `10` | changes `nice` value* for ffmpeg subprocess. available only on unix systems. |
| `TIKTOK_DEVICE_INFO` | | *see below* | device info (including `iid` and `device_id`) for tiktok functionality. required for tiktok to work. |
\* the higher the nice value, the lower the priority. [read more here](https://en.wikipedia.org/wiki/Nice_(Unix)).
#### TIKTOK_DEVICE_INFO
you need to get your own device info for tiktok functionality to work. this can be done by proxying the app through any request-intercepting proxy (such as [mitmproxy](https://mitmproxy.org)). you need to disable ssl pinning to see requests. there will be no assistance provided by cobalt for this.
example config (replace **ALL** values with ones you got from mitm):
```json
'{
"iid": "<install_id here>",
"device_id": "<device_id here>",
"channel": "googleplay",
"app_name": "musical_ly",
"version_code": "310503",
"device_platform": "android",
"device_type": "Redmi+7",
"os_version": "13"
}'
```
you can compress the json to save space. if you're using a `.env` file then the line would would look like this (***note the quotes***):
```
TIKTOK_DEVICE_INFO='{"iid":"<install_id here>","device_id":"<device_id here>","channel":"googleplay","app_name":"musical_ly","version_code":"310503","device_platform":"android","device_type":"Redmi+7","os_version":"13"}'
```
### variables for web
| variable name | default | example | description |
|:---------------------|:---------------------|:------------------------|:--------------------------------------------------------------------------------------|

View file

@ -1,7 +1,7 @@
{
"name": "cobalt",
"description": "save what you love",
"version": "7.12.1",
"version": "7.12.2",
"author": "wukko",
"exports": "./src/cobalt.js",
"type": "module",

View file

@ -452,6 +452,7 @@ button:active,
display: flex;
flex-direction: column;
gap: 18px;
width: 100%;
}
.popup.small.visible {
transform: translate(-50%, -50%);

View file

@ -1,7 +1,7 @@
{
"name": "english",
"substrings": {
"ContactLink": "check the <a class=\"text-backdrop link\" href=\"{statusPage}\" target=\"_blank\">status page</a> or <a class=\"text-backdrop link\" href=\"{repo}\" target=\"_blank\">create an issue on github</a>."
"ContactLink": "check the <a class=\"text-backdrop link\" href=\"{statusPage}\" target=\"_blank\">status page</a> or <a class=\"text-backdrop link\" href=\"{repo}\" target=\"_blank\">create an issue on github</a>"
},
"strings": {
"AppTitleCobalt": "cobalt",

View file

@ -83,10 +83,8 @@ export default async function(host, patternMatch, url, lang, obj) {
id: patternMatch.id
});
break;
case "douyin":
case "tiktok":
r = await tiktok({
host: host,
postId: patternMatch.postId,
id: patternMatch.id,
fullAudio: obj.isTTFullAudio,

View file

@ -1,42 +1,21 @@
import { genericUserAgent } from "../../config.js";
const userAgent = genericUserAgent.split(' Chrome/1')[0],
config = {
tiktok: {
short: "https://vt.tiktok.com/",
api: "https://api22-normal-c-useast2a.tiktokv.com/aweme/v1/feed/?aweme_id={postId}&version_code=262&app_name=musical_ly&channel=App&device_id=null&os_version=14.4.2&device_platform=iphone&device_type=iPhone9&region=US&carrier_region=US",
userAgent: "TikTok 26.2.0 rv:262018 (iPhone; iOS 14.4.2; en_US) Cronet"
},
douyin: {
short: "https://v.douyin.com/",
api: "https://www.iesdouyin.com/aweme/v1/web/aweme/detail/?aweme_id={postId}",
userAgent: "TikTok 26.2.0 rv:262018 (iPhone; iOS 14.4.2; en_US) Cronet"
}
}
function selector(j, h, id) {
if (!j) return false;
let t;
switch (h) {
case "tiktok":
t = j.aweme_list.filter(v => v.aweme_id === id)[0];
break;
case "douyin":
t = j.aweme_detail;
break;
}
if (t?.length < 3) return false;
return t;
}
const shortDomain = "https://vt.tiktok.com/";
const apiPath = "https://api22-normal-c-alisg.tiktokv.com/aweme/v1/feed/?region=US&carrier_region=US";
const apiUserAgent = "TikTok/338014 CFNetwork/1410.1 Darwin/22.6.0";
export default async function(obj) {
let postId = obj.postId ? obj.postId : false;
if (!process.env.TIKTOK_DEVICE_INFO) return { error: 'ErrorCouldntFetch' };
if (!postId) {
let html = await fetch(`${config[obj.host].short}${obj.id}`, {
let html = await fetch(`${shortDomain}${obj.id}`, {
redirect: "manual",
headers: { "user-agent": userAgent }
}).then((r) => { return r.text() }).catch(() => { return false });
headers: {
"user-agent": genericUserAgent.split(' Chrome/1')[0]
}
}).then(r => r.text()).catch(() => {});
if (!html) return { error: 'ErrorCouldntFetch' };
@ -48,30 +27,35 @@ export default async function(obj) {
}
if (!postId) return { error: 'ErrorCantGetID' };
let detail;
detail = await fetch(config[obj.host].api.replace("{postId}", postId), {
headers: {
"user-agent": config[obj.host].userAgent
}
}).then((r) => { return r.json() }).catch(() => { return false });
let deviceInfo = JSON.parse(process.env.TIKTOK_DEVICE_INFO);
deviceInfo = new URLSearchParams(deviceInfo).toString();
detail = selector(detail, obj.host, postId);
let apiURL = new URL(apiPath);
apiURL.searchParams.append("aweme_id", postId);
let detail = await fetch(`${apiURL.href}&${deviceInfo}`, {
headers: {
"user-agent": apiUserAgent
}
}).then(r => r.json()).catch(() => {});
detail = detail?.aweme_list?.find(v => v.aweme_id === postId);
if (!detail) return { error: 'ErrorCouldntFetch' };
let video, videoFilename, audioFilename, isMp3, audio, images,
filenameBase = `${obj.host}_${detail.author.unique_id}_${postId}`;
filenameBase = `tiktok_${detail.author.unique_id}_${postId}`;
if (obj.host === "tiktok") {
images = detail.image_post_info ? detail.image_post_info.images : false
} else {
images = detail.images ? detail.images : false
}
images = detail.image_post_info?.images;
let playAddr = detail.video.play_addr_h264;
if (!playAddr) playAddr = detail.video.play_addr;
if (!obj.isAudioOnly && !images) {
video = detail.video.play_addr.url_list[0];
video = playAddr.url_list[0];
videoFilename = `${filenameBase}.mp4`;
} else {
let fallback = detail.video.play_addr.url_list[0];
let fallback = playAddr.url_list[0];
audio = fallback;
audioFilename = `${filenameBase}_audio_fv`; // fv - from video
if (obj.fullAudio || fallback.includes("music")) {
@ -94,7 +78,7 @@ export default async function(obj) {
if (images) {
let imageLinks = [];
for (let i in images) {
let sel = obj.host === "tiktok" ? images[i].display_image.url_list : images[i].url_list;
let sel = images[i].display_image.url_list;
sel = sel.filter(p => p.includes(".jpeg?"))
imageLinks.push({url: sel[0]})
}