mirror of
https://github.com/imputnet/cobalt.git
synced 2025-07-18 11:18:28 +00:00
Merge branch 'wukko:current' into ddinstagram
This commit is contained in:
commit
fbc026f4e4
13
README.md
13
README.md
@ -1,7 +1,7 @@
|
||||
# cobalt
|
||||
best way to save what you love: [cobalt.tools](https://cobalt.tools/)
|
||||
|
||||
 pattern background")
|
||||
 pattern background")
|
||||
|
||||
## what's cobalt?
|
||||
cobalt is a media downloader that doesn't piss you off. it's fast, friendly, and doesn't have any bullshit that modern web is filled with: ***no ads, trackers, or invasive analytics***.
|
||||
@ -51,12 +51,15 @@ this list is not final and keeps expanding over time. if support for a service y
|
||||
| youtube | supports videos, music, and shorts. 8K, 4K, HDR, VR, and high FPS videos. rich metadata & dubs. h264/av1/vp9 codecs. |
|
||||
|
||||
## cobalt api
|
||||
cobalt has an open api that you can use in projects *for completely free~*. it's easy and straightforward to use, [check out the docs](https://github.com/wukko/cobalt/blob/current/docs/api.md) to learn how to use it.
|
||||
cobalt has an open api that you can use in your projects *for free~*. it's easy and straightforward to use, [check out the docs](/docs/api.md) to learn how to use it.
|
||||
|
||||
you can use the main api instance ([co.wuk.sh](https://co.wuk.sh/)) in your projects.
|
||||
✅ you can use the main api instance ([co.wuk.sh](https://co.wuk.sh/)) in your **personal** projects.
|
||||
❌ you cannot use the free api commercially (anywhere that's gated behind paywalls or ads). host your own instance for this.
|
||||
|
||||
we reserve the right to restrict abusive/excessive access to the main instance api.
|
||||
|
||||
## how to run your own instance
|
||||
if you want to run your own instance for whatever purpose, [follow this guide](https://github.com/wukko/cobalt/blob/current/docs/run-an-instance.md).
|
||||
if you want to run your own instance for whatever purpose, [follow this guide](/docs/run-an-instance.md).
|
||||
it's *highly* recommended to use a docker compose method unless you run for developing/debugging purposes.
|
||||
|
||||
## sponsors
|
||||
@ -68,7 +71,7 @@ cobalt is a tool for easing content downloads from internet and takes ***zero li
|
||||
cobalt is ***NOT*** a piracy tool and cannot be used as such. it can only download free, publicly accessible content. such content can be easily downloaded through any browser's dev tools. pressing one button is easier, so i made a convenient, ad-less tool for such repeated actions.
|
||||
|
||||
## cobalt license
|
||||
cobalt code is licensed under [AGPL-3.0](https://github.com/wukko/cobalt/blob/current/LICENSE).
|
||||
cobalt code is licensed under [AGPL-3.0](/LICENSE).
|
||||
|
||||
cobalt branding, mascots, and other related assets included in the repo are ***copyrighted*** and not covered by the AGPL-3.0 license. you ***cannot*** use them under same terms.
|
||||
|
||||
|
@ -59,7 +59,7 @@ from a successful call to `/api/json`. however, the parameters passed to it are
|
||||
and **unmodifiable** from your (the api client's) perspective, and can change between versions.
|
||||
|
||||
therefore you don't need to worry about what they mean - but if you really want to know, you can
|
||||
[read the source code](../src/modules/stream/manage.js).
|
||||
[read the source code](/src/modules/stream/manage.js).
|
||||
|
||||
## GET: `/api/serverInfo`
|
||||
returns current basic server info.
|
||||
|
@ -21,6 +21,8 @@ services:
|
||||
API_URL: "https://co.wuk.sh/"
|
||||
# replace eu-nl with your instance's distinctive name
|
||||
API_NAME: "eu-nl"
|
||||
# if you want to use cookies when fetching data from services, uncomment the next line and the lines under volume
|
||||
# COOKIE_PATH: "/cookies.json"
|
||||
# see docs/run-an-instance.md for more information
|
||||
labels:
|
||||
- com.centurylinklabs.watchtower.scope=cobalt
|
||||
|
BIN
docs/images/troubleshooting/clipboard/config.png
Normal file
BIN
docs/images/troubleshooting/clipboard/config.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
BIN
docs/images/troubleshooting/clipboard/risk.png
Normal file
BIN
docs/images/troubleshooting/clipboard/risk.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
BIN
docs/images/troubleshooting/clipboard/search.png
Normal file
BIN
docs/images/troubleshooting/clipboard/search.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
BIN
docs/images/troubleshooting/clipboard/toggle.png
Normal file
BIN
docs/images/troubleshooting/clipboard/toggle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
BIN
docs/images/troubleshooting/clipboard/toggled.png
Normal file
BIN
docs/images/troubleshooting/clipboard/toggled.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
@ -18,7 +18,7 @@ if you need help with installing docker, follow *only the first step* of these t
|
||||
```
|
||||
i'm using `nano` in this example, it may not be available in your distro. you can use any other text editor.
|
||||
|
||||
3. copy and paste the [sample config from here](https://github.com/wukko/cobalt/blob/current/docs/examples/docker-compose.example.yml) for either web or api instance (or both, if you wish) and edit it to your needs.
|
||||
3. copy and paste the [sample config from here](examples/docker-compose.example.yml) for either web or api instance (or both, if you wish) and edit it to your needs.
|
||||
make sure to replace default URLs with your own or cobalt won't work correctly.
|
||||
|
||||
4. finally, start the cobalt container (from cobalt directory):
|
||||
@ -26,7 +26,7 @@ if you need help with installing docker, follow *only the first step* of these t
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
if you want your instance to support services that require authentication to view public content, create `cookies.json` file in the same directory as `docker-compose.yml`. example cookies file [can be found here](https://github.com/wukko/cobalt/blob/current/docs/examples/cookies.example.json).
|
||||
if you want your instance to support services that require authentication to view public content, create `cookies.json` file in the same directory as `docker-compose.yml`. example cookies file [can be found here](examples/cookies.example.json).
|
||||
|
||||
cobalt package will update automatically thanks to watchtower.
|
||||
|
||||
|
@ -5,29 +5,33 @@
|
||||
if any issues occur while using cobalt, you can fix many of them yourself. this document aims to provide guides on how to fix most complicated of them.
|
||||
use wiki navigation on right to jump between solutions.
|
||||
|
||||
## how to fix clipboard pasting in firefox
|
||||
## how to fix clipboard pasting in older versions of firefox
|
||||
```
|
||||
🎉 firefox finally supports pasting by default starting from version 125.
|
||||
|
||||
👍 you don't need to follow this tutorial if you're on the latest version of firefox.
|
||||
```
|
||||
you can fix this issue by changing a single preference in `about:config`.
|
||||
|
||||
### steps to enable clipboard functionality
|
||||
1. go to `about:config`:
|
||||
|
||||

|
||||

|
||||
|
||||
2. if asked, read what firefox has to say and press "accept the risk and continue".
|
||||
⚠ tinkering with other preferences may break your browser. **do not** edit them unless you know what you're doing.
|
||||
|
||||

|
||||

|
||||
|
||||
3. search for `dom.events.asyncClipboard.readText`
|
||||
|
||||

|
||||

|
||||
|
||||
4. press the toggle button on very right.
|
||||
|
||||

|
||||

|
||||
|
||||
5. "false" should change to "true".
|
||||
|
||||

|
||||

|
||||
|
||||
6. go back to cobalt, reload the page, press `paste and download` button again. this time it works! enjoy simpler downloading experience :)
|
||||
6. go back to cobalt, reload the page, press `paste` button again. this time it works! enjoy simpler downloading experience :)
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cobalt",
|
||||
"description": "save what you love",
|
||||
"version": "7.12.5",
|
||||
"version": "7.12.6",
|
||||
"author": "wukko",
|
||||
"exports": "./src/cobalt.js",
|
||||
"type": "module",
|
||||
|
@ -60,36 +60,64 @@ async function request(url, cookie, method = 'GET', requestData) {
|
||||
return data.json();
|
||||
}
|
||||
|
||||
async function getPost(id) {
|
||||
let data;
|
||||
try {
|
||||
const cookie = getCookie('instagram');
|
||||
let dtsgId;
|
||||
|
||||
if (cookie) {
|
||||
dtsgId = await findDtsgId(cookie);
|
||||
async function requestHTML(id, cookie) {
|
||||
const data = await fetch(`https://www.instagram.com/p/${id}/embed/captioned/`, {
|
||||
headers: {
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
||||
"Accept-Language": "en-GB,en;q=0.9",
|
||||
"Cache-Control": "max-age=0",
|
||||
"Dnt": "1",
|
||||
"Priority": "u=0, i",
|
||||
"Sec-Ch-Ua": 'Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99',
|
||||
"Sec-Ch-Ua-Mobile": "?0",
|
||||
"Sec-Ch-Ua-Platform": "macOS",
|
||||
"Sec-Fetch-Dest": "document",
|
||||
"Sec-Fetch-Mode": "navigate",
|
||||
"Sec-Fetch-Site": "none",
|
||||
"Sec-Fetch-User": "?1",
|
||||
"Upgrade-Insecure-Requests": "1",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
|
||||
...cookie
|
||||
}
|
||||
}).then(r => r.text());
|
||||
|
||||
const url = new URL('https://www.instagram.com/graphql/query/');
|
||||
let embedData = JSON.parse(data?.match(/"init",\[\],\[(.*?)\]\],/)[1]);
|
||||
|
||||
const requestData = {
|
||||
jazoest: '26297',
|
||||
variables: JSON.stringify({
|
||||
shortcode: id,
|
||||
__relay_internal__pv__PolarisShareMenurelayprovider: false
|
||||
}),
|
||||
doc_id: '24852649951017035'
|
||||
};
|
||||
if (dtsgId) {
|
||||
requestData.fb_dtsg = dtsgId;
|
||||
}
|
||||
if (!embedData || !embedData?.contextJSON) return false;
|
||||
|
||||
data = (await request(url, cookie, 'POST', requestData)).data;
|
||||
} catch {}
|
||||
embedData = JSON.parse(embedData.contextJSON);
|
||||
|
||||
if (!data) return { error: 'ErrorCouldntFetch' };
|
||||
return embedData;
|
||||
}
|
||||
async function requestGQL(id, cookie) {
|
||||
let dtsgId;
|
||||
|
||||
const sidecar = data?.xdt_shortcode_media?.edge_sidecar_to_children;
|
||||
if (cookie) {
|
||||
dtsgId = await findDtsgId(cookie);
|
||||
}
|
||||
const url = new URL('https://www.instagram.com/api/graphql/');
|
||||
|
||||
const requestData = {
|
||||
jazoest: '26406',
|
||||
variables: JSON.stringify({
|
||||
shortcode: id,
|
||||
__relay_internal__pv__PolarisShareMenurelayprovider: false
|
||||
}),
|
||||
doc_id: '7153618348081770'
|
||||
};
|
||||
if (dtsgId) {
|
||||
requestData.fb_dtsg = dtsgId;
|
||||
}
|
||||
|
||||
return (await request(url, cookie, 'POST', requestData))
|
||||
.data
|
||||
?.xdt_api__v1__media__shortcode__web_info
|
||||
?.items
|
||||
?.[0];
|
||||
}
|
||||
|
||||
function extractOldPost(data, id) {
|
||||
const sidecar = data?.gql_data?.shortcode_media?.edge_sidecar_to_children;
|
||||
if (sidecar) {
|
||||
const picker = sidecar.edges.filter(e => e.node?.display_url)
|
||||
.map(e => {
|
||||
@ -110,19 +138,86 @@ async function getPost(id) {
|
||||
});
|
||||
|
||||
if (picker.length) return { picker }
|
||||
} else if (data?.xdt_shortcode_media?.video_url) {
|
||||
} else if (data?.gql_data?.shortcode_media?.video_url) {
|
||||
return {
|
||||
urls: data.xdt_shortcode_media.video_url,
|
||||
urls: data.gql_data.shortcode_media.video_url,
|
||||
filename: `instagram_${id}.mp4`,
|
||||
audioFilename: `instagram_${id}_audio`
|
||||
}
|
||||
} else if (data?.xdt_shortcode_media?.display_url) {
|
||||
} else if (data?.gql_data?.shortcode_media?.display_url) {
|
||||
return {
|
||||
urls: data.xdt_shortcode_media.display_url,
|
||||
urls: data.gql_data?.shortcode_media.display_url,
|
||||
isPhoto: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function extractNewPost(data, id) {
|
||||
const carousel = data.carousel_media;
|
||||
if (carousel) {
|
||||
const picker = carousel.filter(e => e?.image_versions2)
|
||||
.map(e => {
|
||||
const type = e.video_versions ? "video" : "photo";
|
||||
const imageUrl = e.image_versions2.candidates[0].url;
|
||||
|
||||
let url = imageUrl;
|
||||
if (type === 'video') {
|
||||
const video = e.video_versions.reduce((a, b) => a.width * a.height < b.width * b.height ? b : a);
|
||||
url = video.url;
|
||||
}
|
||||
|
||||
return {
|
||||
type, url,
|
||||
/* thumbnails have `Cross-Origin-Resource-Policy`
|
||||
** set to `same-origin`, so we need to proxy them */
|
||||
thumb: createStream({
|
||||
service: "instagram",
|
||||
type: "default",
|
||||
u: imageUrl,
|
||||
filename: "image.jpg"
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
if (picker.length) return { picker }
|
||||
} else if (data.video_versions) {
|
||||
const video = data.video_versions.reduce((a, b) => a.width * a.height < b.width * b.height ? b : a)
|
||||
return {
|
||||
urls: video.url,
|
||||
filename: `instagram_${id}.mp4`,
|
||||
audioFilename: `instagram_${id}_audio`
|
||||
}
|
||||
} else if (data.image_versions2?.candidates) {
|
||||
return {
|
||||
urls: data.image_versions2.candidates[0].url,
|
||||
isPhoto: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getPost(id) {
|
||||
let data, result, dataType = 'old';
|
||||
try {
|
||||
const cookie = getCookie('instagram');
|
||||
|
||||
data = await requestHTML(id);
|
||||
if (!data && cookie) data = await requestHTML(id, cookie);
|
||||
|
||||
if (!data) {
|
||||
dataType = 'new';
|
||||
data = await requestGQL(id, cookie);
|
||||
}
|
||||
} catch {}
|
||||
|
||||
if (!data) return { error: 'ErrorCouldntFetch' };
|
||||
|
||||
if (dataType === 'new') {
|
||||
result = extractNewPost(data, id)
|
||||
} else {
|
||||
result = extractOldPost(data, id)
|
||||
}
|
||||
|
||||
if (result) return result;
|
||||
return { error: 'ErrorEmptyDownload' }
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user