Merge branch 'wukko:current' into ddinstagram

This commit is contained in:
hyperdefined 2024-04-22 17:32:12 -04:00 committed by GitHub
commit fbc026f4e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 149 additions and 45 deletions

View File

@ -1,7 +1,7 @@
# cobalt # cobalt
best way to save what you love: [cobalt.tools](https://cobalt.tools/) best way to save what you love: [cobalt.tools](https://cobalt.tools/)
![cobalt logo with repeated logo (double arrow) pattern background](https://raw.githubusercontent.com/wukko/cobalt/current/src/front/icons/pattern.png "cobalt logo with repeated logo (double arrow) pattern background") ![cobalt logo with repeated logo (double arrow) pattern background](/src/front/icons/pattern.png "cobalt logo with repeated logo (double arrow) pattern background")
## what's cobalt? ## 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***. 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. | | youtube | supports videos, music, and shorts. 8K, 4K, HDR, VR, and high FPS videos. rich metadata & dubs. h264/av1/vp9 codecs. |
## cobalt api ## 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 ## 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. it's *highly* recommended to use a docker compose method unless you run for developing/debugging purposes.
## sponsors ## 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 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 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. 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.

View File

@ -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. 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 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` ## GET: `/api/serverInfo`
returns current basic server info. returns current basic server info.

View File

@ -21,6 +21,8 @@ services:
API_URL: "https://co.wuk.sh/" API_URL: "https://co.wuk.sh/"
# replace eu-nl with your instance's distinctive name # replace eu-nl with your instance's distinctive name
API_NAME: "eu-nl" 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 # see docs/run-an-instance.md for more information
labels: labels:
- com.centurylinklabs.watchtower.scope=cobalt - com.centurylinklabs.watchtower.scope=cobalt

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -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. 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. make sure to replace default URLs with your own or cobalt won't work correctly.
4. finally, start the cobalt container (from cobalt directory): 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 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. cobalt package will update automatically thanks to watchtower.

View File

@ -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. 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. 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`. you can fix this issue by changing a single preference in `about:config`.
### steps to enable clipboard functionality ### steps to enable clipboard functionality
1. go to `about:config`: 1. go to `about:config`:
![screenshot showing about:config entered into address bar](images/troubleshooting/clipboard/config.png)
![screenshot showing about:config entered into address bar](https://github.com/wukko/cobalt/assets/71202418/9ad78612-a372-4949-aeac-99dfc41e273c)
2. if asked, read what firefox has to say and press "accept the risk and continue". 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. ⚠ tinkering with other preferences may break your browser. **do not** edit them unless you know what you're doing.
![screenshot showing about:config security warning that reads: "proceed with caution. changing advanced configuration preferences can impact firefox performance or security." lower there's a pre-checked checkbox that says: "warn me when i attempt to access these preferences". lowest element is a blue button that says "accept the risk and continue"](https://github.com/wukko/cobalt/assets/71202418/02328729-dbfe-4ea4-b2ca-7bcf1998c2ca) ![screenshot showing about:config security warning that reads: "proceed with caution. changing advanced configuration preferences can impact firefox performance or security." lower there's a pre-checked checkbox that says: "warn me when i attempt to access these preferences". lowest element is a blue button that says "accept the risk and continue"](images/troubleshooting/clipboard/risk.png)
3. search for `dom.events.asyncClipboard.readText` 3. search for `dom.events.asyncClipboard.readText`
![screenshot showing "dom.events.asyncclipboard.readtext" entered into search on about:config page](https://github.com/wukko/cobalt/assets/71202418/7c7f7e3c-6a6a-40df-8436-277489e72e0b) ![screenshot showing "dom.events.asyncclipboard.readtext" entered into search on about:config page](images/troubleshooting/clipboard/search.png)
4. press the toggle button on very right. 4. press the toggle button on very right.
![screenshot showing "dom.events.asyncclipboard.readtext" preference on about:config page with highlighted toggle button on very right](https://github.com/wukko/cobalt/assets/71202418/b45db18e-f4bf-4f1c-9a8c-f13a63a21335) ![screenshot showing "dom.events.asyncclipboard.readtext" preference on about:config page with highlighted toggle button on very right](images/troubleshooting/clipboard/toggle.png)
5. "false" should change to "true". 5. "false" should change to "true".
![screenshot showing "dom.events.asyncclipboard.readtext" preference on about:config page, this one with "true" text highlighted](https://github.com/wukko/cobalt/assets/71202418/4869b4ff-8385-4cd3-ae59-aa2e03a58b5f) ![screenshot showing "dom.events.asyncclipboard.readtext" preference on about:config page, this one with "true" text highlighted](images/troubleshooting/clipboard/toggled.png)
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 :)

View File

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

View File

@ -60,36 +60,64 @@ async function request(url, cookie, method = 'GET', requestData) {
return data.json(); return data.json();
} }
async function getPost(id) { async function requestHTML(id, cookie) {
let data; const data = await fetch(`https://www.instagram.com/p/${id}/embed/captioned/`, {
try { headers: {
const cookie = getCookie('instagram'); "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());
let embedData = JSON.parse(data?.match(/"init",\[\],\[(.*?)\]\],/)[1]);
if (!embedData || !embedData?.contextJSON) return false;
embedData = JSON.parse(embedData.contextJSON);
return embedData;
}
async function requestGQL(id, cookie) {
let dtsgId; let dtsgId;
if (cookie) { if (cookie) {
dtsgId = await findDtsgId(cookie); dtsgId = await findDtsgId(cookie);
} }
const url = new URL('https://www.instagram.com/api/graphql/');
const url = new URL('https://www.instagram.com/graphql/query/');
const requestData = { const requestData = {
jazoest: '26297', jazoest: '26406',
variables: JSON.stringify({ variables: JSON.stringify({
shortcode: id, shortcode: id,
__relay_internal__pv__PolarisShareMenurelayprovider: false __relay_internal__pv__PolarisShareMenurelayprovider: false
}), }),
doc_id: '24852649951017035' doc_id: '7153618348081770'
}; };
if (dtsgId) { if (dtsgId) {
requestData.fb_dtsg = dtsgId; requestData.fb_dtsg = dtsgId;
} }
data = (await request(url, cookie, 'POST', requestData)).data; return (await request(url, cookie, 'POST', requestData))
} catch {} .data
?.xdt_api__v1__media__shortcode__web_info
?.items
?.[0];
}
if (!data) return { error: 'ErrorCouldntFetch' }; function extractOldPost(data, id) {
const sidecar = data?.gql_data?.shortcode_media?.edge_sidecar_to_children;
const sidecar = data?.xdt_shortcode_media?.edge_sidecar_to_children;
if (sidecar) { if (sidecar) {
const picker = sidecar.edges.filter(e => e.node?.display_url) const picker = sidecar.edges.filter(e => e.node?.display_url)
.map(e => { .map(e => {
@ -110,19 +138,86 @@ async function getPost(id) {
}); });
if (picker.length) return { picker } if (picker.length) return { picker }
} else if (data?.xdt_shortcode_media?.video_url) { } else if (data?.gql_data?.shortcode_media?.video_url) {
return { return {
urls: data.xdt_shortcode_media.video_url, urls: data.gql_data.shortcode_media.video_url,
filename: `instagram_${id}.mp4`, filename: `instagram_${id}.mp4`,
audioFilename: `instagram_${id}_audio` audioFilename: `instagram_${id}_audio`
} }
} else if (data?.xdt_shortcode_media?.display_url) { } else if (data?.gql_data?.shortcode_media?.display_url) {
return { return {
urls: data.xdt_shortcode_media.display_url, urls: data.gql_data?.shortcode_media.display_url,
isPhoto: true 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' } return { error: 'ErrorEmptyDownload' }
} }