mirror of
https://github.com/wukko/cobalt.git
synced 2025-02-10 20:56:25 +01:00
fix tiktok downloads (#407)
This commit is contained in:
commit
36c697cc5f
6 changed files with 57 additions and 51 deletions
|
@ -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`. |
|
| `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. |
|
| `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. |
|
| `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)).
|
\* 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
|
### variables for web
|
||||||
| variable name | default | example | description |
|
| variable name | default | example | description |
|
||||||
|:---------------------|:---------------------|:------------------------|:--------------------------------------------------------------------------------------|
|
|:---------------------|:---------------------|:------------------------|:--------------------------------------------------------------------------------------|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "cobalt",
|
"name": "cobalt",
|
||||||
"description": "save what you love",
|
"description": "save what you love",
|
||||||
"version": "7.12.1",
|
"version": "7.12.2",
|
||||||
"author": "wukko",
|
"author": "wukko",
|
||||||
"exports": "./src/cobalt.js",
|
"exports": "./src/cobalt.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|
|
@ -452,6 +452,7 @@ button:active,
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 18px;
|
gap: 18px;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
.popup.small.visible {
|
.popup.small.visible {
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "english",
|
"name": "english",
|
||||||
"substrings": {
|
"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": {
|
"strings": {
|
||||||
"AppTitleCobalt": "cobalt",
|
"AppTitleCobalt": "cobalt",
|
||||||
|
|
|
@ -83,10 +83,8 @@ export default async function(host, patternMatch, url, lang, obj) {
|
||||||
id: patternMatch.id
|
id: patternMatch.id
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "douyin":
|
|
||||||
case "tiktok":
|
case "tiktok":
|
||||||
r = await tiktok({
|
r = await tiktok({
|
||||||
host: host,
|
|
||||||
postId: patternMatch.postId,
|
postId: patternMatch.postId,
|
||||||
id: patternMatch.id,
|
id: patternMatch.id,
|
||||||
fullAudio: obj.isTTFullAudio,
|
fullAudio: obj.isTTFullAudio,
|
||||||
|
|
|
@ -1,42 +1,21 @@
|
||||||
import { genericUserAgent } from "../../config.js";
|
import { genericUserAgent } from "../../config.js";
|
||||||
|
|
||||||
const userAgent = genericUserAgent.split(' Chrome/1')[0],
|
const shortDomain = "https://vt.tiktok.com/";
|
||||||
config = {
|
const apiPath = "https://api22-normal-c-alisg.tiktokv.com/aweme/v1/feed/?region=US&carrier_region=US";
|
||||||
tiktok: {
|
const apiUserAgent = "TikTok/338014 CFNetwork/1410.1 Darwin/22.6.0";
|
||||||
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®ion=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;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function(obj) {
|
export default async function(obj) {
|
||||||
let postId = obj.postId ? obj.postId : false;
|
let postId = obj.postId ? obj.postId : false;
|
||||||
|
|
||||||
|
if (!process.env.TIKTOK_DEVICE_INFO) return { error: 'ErrorCouldntFetch' };
|
||||||
|
|
||||||
if (!postId) {
|
if (!postId) {
|
||||||
let html = await fetch(`${config[obj.host].short}${obj.id}`, {
|
let html = await fetch(`${shortDomain}${obj.id}`, {
|
||||||
redirect: "manual",
|
redirect: "manual",
|
||||||
headers: { "user-agent": userAgent }
|
headers: {
|
||||||
}).then((r) => { return r.text() }).catch(() => { return false });
|
"user-agent": genericUserAgent.split(' Chrome/1')[0]
|
||||||
|
}
|
||||||
|
}).then(r => r.text()).catch(() => {});
|
||||||
|
|
||||||
if (!html) return { error: 'ErrorCouldntFetch' };
|
if (!html) return { error: 'ErrorCouldntFetch' };
|
||||||
|
|
||||||
|
@ -48,30 +27,35 @@ export default async function(obj) {
|
||||||
}
|
}
|
||||||
if (!postId) return { error: 'ErrorCantGetID' };
|
if (!postId) return { error: 'ErrorCantGetID' };
|
||||||
|
|
||||||
let detail;
|
let deviceInfo = JSON.parse(process.env.TIKTOK_DEVICE_INFO);
|
||||||
detail = await fetch(config[obj.host].api.replace("{postId}", postId), {
|
deviceInfo = new URLSearchParams(deviceInfo).toString();
|
||||||
headers: {
|
|
||||||
"user-agent": config[obj.host].userAgent
|
|
||||||
}
|
|
||||||
}).then((r) => { return r.json() }).catch(() => { return false });
|
|
||||||
|
|
||||||
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' };
|
if (!detail) return { error: 'ErrorCouldntFetch' };
|
||||||
|
|
||||||
let video, videoFilename, audioFilename, isMp3, audio, images,
|
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?.images;
|
||||||
images = detail.image_post_info ? detail.image_post_info.images : false
|
|
||||||
} else {
|
let playAddr = detail.video.play_addr_h264;
|
||||||
images = detail.images ? detail.images : false
|
|
||||||
}
|
if (!playAddr) playAddr = detail.video.play_addr;
|
||||||
|
|
||||||
if (!obj.isAudioOnly && !images) {
|
if (!obj.isAudioOnly && !images) {
|
||||||
video = detail.video.play_addr.url_list[0];
|
video = playAddr.url_list[0];
|
||||||
videoFilename = `${filenameBase}.mp4`;
|
videoFilename = `${filenameBase}.mp4`;
|
||||||
} else {
|
} else {
|
||||||
let fallback = detail.video.play_addr.url_list[0];
|
let fallback = playAddr.url_list[0];
|
||||||
audio = fallback;
|
audio = fallback;
|
||||||
audioFilename = `${filenameBase}_audio_fv`; // fv - from video
|
audioFilename = `${filenameBase}_audio_fv`; // fv - from video
|
||||||
if (obj.fullAudio || fallback.includes("music")) {
|
if (obj.fullAudio || fallback.includes("music")) {
|
||||||
|
@ -94,7 +78,7 @@ export default async function(obj) {
|
||||||
if (images) {
|
if (images) {
|
||||||
let imageLinks = [];
|
let imageLinks = [];
|
||||||
for (let i in images) {
|
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?"))
|
sel = sel.filter(p => p.includes(".jpeg?"))
|
||||||
imageLinks.push({url: sel[0]})
|
imageLinks.push({url: sel[0]})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue