diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 00000000..e5429401 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,32 @@ +--- +name: bug report +about: report an issue with downloads or something else +title: '' +labels: bug +assignees: '' + +--- + +**bug description** +a clear and concise description of what the bug is. + +**reproduction steps** +steps to reproduce the behavior: +1. go to '...' +2. click on '....' +3. download this video: **[link here]** +4. see error + +**screenshots** +if applicable, add screenshots or screen recordings to help explain your problem. + +**links** +if applicable, add links that cause the issue. more = better. + +**platform** +- OS [e.g. iOS, windows] +- browser [e.g. chrome, safari, firefox] +- version [e.g. 115] + +**additional context** +add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 00000000..18307f4f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,17 @@ +--- +name: feature request +about: suggest a feature for cobalt +title: '' +labels: feature request +assignees: '' + +--- + +**describe the feature you'd like to see** +a clear and concise description of what you want to happen. + +**describe alternatives you've considered** +a clear and concise description of any alternative solutions or features you've considered. + +**additional context** +add any other context or screenshots about the feature request here. diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index d70b1821..8b7042d9 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -29,20 +29,22 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Get version from package.json - id: package-version - uses: martinbeentjes/npm-get-version-action@v1.3.1 - - name: Get short commit hash - id: commit-hash - run: echo "commit_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + - name: Get release metadata + id: release-meta + run: | + version=$(cat package.json | jq -r .version) + echo "commit_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + echo "version=$version" >> $GITHUB_OUTPUT + echo "major_version=$(echo "$version" | cut -d. -f1)" >> $GITHUB_OUTPUT - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@v4 with: tags: | type=raw,value=latest - type=raw,value=${{ steps.package-version.outputs.current-version }} - type=raw,value=${{ steps.package-version.outputs.current-version }}-${{ steps.commit-hash.outputs.commit_short }} + type=raw,value=${{ steps.release-meta.outputs.version }} + type=raw,value=${{ steps.release-meta.outputs.major_version }} + type=raw,value=${{ steps.release-meta.outputs.version }}-${{ steps.release-meta.outputs.commit_short }} images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - name: Build and push Docker image diff --git a/.gitignore b/.gitignore index 887344cc..a21273d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# os stuff +.DS_Store +desktop.ini + # npm node_modules package-lock.json diff --git a/README.md b/README.md index be0429df..16998478 100644 --- a/README.md +++ b/README.md @@ -1,78 +1,80 @@ # cobalt -Best way to save what you love. -Live web app: [cobalt.tools](https://cobalt.tools/) +best way to save what you love: [cobalt.tools](https://cobalt.tools/) -![cobalt logo with repeated logo pattern background](https://raw.githubusercontent.com/wukko/cobalt/current/src/front/icons/pattern.png "cobalt logo with repeated logo pattern background") +![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") -[![DeepSource](https://deepsource.io/gh/wukko/cobalt.svg/?label=active+issues&token=MsmsJ9zUOKwcQor0yaiFot84)](https://deepsource.io/gh/wukko/cobalt/?ref=repository-badge) +## 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 analytics***. -## What's cobalt? -cobalt is social and media platform downloader that doesn't piss you off. +paste the link, get the file, move on. it's that simple. just how it should be. -It's fast, friendly, and doesn't have any bullshit that modern web is filled with: no ads, trackers, or analytics. -Paste the link, get the video, move on. It's that simple. Just how it should be. +## supported services +this list is not final and keeps expanding over time. if support for a service you want is missing, create an issue (or a pull request 👀). -## Supported services -| Service | Video + Audio | Only audio | Only video | Additional notes or features | -| -------- | :---: | :---: | :---: | :----- | -| bilibili.com | ✅ | ✅ | ✅ | | -| Instagram | ✅ | ✅ | ✅ | Supports photos and videos, lets you pick what to save from multi-media posts. | -| Instagram Reels | ✅ | ✅ | ✅ | | -| Pinterest | ✅ | ✅ | ✅ | Support for videos and stories. | -| Reddit | ✅ | ✅ | ✅ | Support for GIFs and videos. | -| Rutube | ✅ | ✅ | ✅ | | -| SoundCloud | ➖ | ✅ | ➖ | Audio metadata, downloads from private links. | -| Streamable | ✅ | ✅ | ✅ | | -| TikTok | ✅ | ✅ | ✅ | Supports downloads of: videos with or without watermark, images from slideshow without watermark, full (original) audios. | -| Tumblr | ✅ | ✅ | ✅ | Support for audio file downloads. | -| Twitch Clips | ✅ | ✅ | ✅ | | -| Twitter/X * | ✅ | ✅ | ✅ | Ability to pick what to save from multi-media tweets. | -| Vimeo | ✅ | ✅ | ✅ | Audio downloads are only available for dash files. | -| Vine Archive | ✅ | ✅ | ✅ | | -| VK Videos | ✅ | ❌ | ❌ | | -| VK Clips | ✅ | ❌ | ❌ | | -| YouTube Videos & Shorts | ✅ | ✅ | ✅ | Support for 8K, 4K, HDR, VR, and high FPS videos. Audio metadata & dubs. h264/av1/vp9 codecs. | -| YouTube Music | ➖ | ✅ | ➖ | Audio metadata. | +| service | video + audio | only audio | only video | metadata | rich file names | +| :-------- | :-----------: | :--------: | :--------: | :------: | :-------------: | +| bilibili.com | ✅ | ✅ | ✅ | ➖ | ➖ | +| instagram posts & stories | ✅ | ✅ | ✅ | ➖ | ➖ | +| instagram reels | ✅ | ✅ | ✅ | ➖ | ➖ | +| pinterest | ✅ | ✅ | ✅ | ➖ | ➖ | +| reddit | ✅ | ✅ | ✅ | ❌ | ❌ | +| rutube | ✅ | ✅ | ✅ | ✅ | ✅ | +| soundcloud | ➖ | ✅ | ➖ | ✅ | ✅ | +| streamable | ✅ | ✅ | ✅ | ➖ | ➖ | +| tiktok | ✅ | ✅ | ✅ | ❌ | ❌ | +| tumblr | ✅ | ✅ | ✅ | ➖ | ➖ | +| twitch clips | ✅ | ✅ | ✅ | ✅ | ✅ | +| twitter/x | ✅ | ✅ | ✅ | ➖ | ➖ | +| vimeo | ✅ | ✅ | ✅ | ✅ | ✅ | +| vine archive | ✅ | ✅ | ✅ | ➖ | ➖ | +| vk videos & clips | ✅ | ❌ | ❌ | ✅ | ✅ | +| youtube videos, shorts & music | ✅ | ✅ | ✅ | ✅ | ✅ | -This list is not final and keeps expanding over time, make sure to check it once in a while! - -*Reliability of downloads from Twitter is questionable due to its current management. +| emoji | meaning | +| :-----: | :---------------------- | +| ✅ | supported | +| ➖ | impossible/unreasonable | +| ❌ | not supported | -## cobalt API -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](https://github.com/wukko/cobalt/blob/current/docs/API.md) and see for yourself. -Feel free to use the main API instance ([co.wuk.sh](https://co.wuk.sh/)) in your projects. +### additional notes or features (per service) +| service | notes or features | +| :-------- | :----- | +| instagram | supports photos, videos, and stories. lets you pick what to save from multi-media posts. | +| pinterest | supports videos and stories. | +| reddit | supports gifs and videos. | +| soundcloud | supports private links. | +| tiktok | supports videos with or without watermark, images from slideshow without watermark, and full (original) audios. | +| twitter/x | lets you pick what to save from multi-media posts. may not be 100% reliable due to current management. | +| vimeo | audio downloads are only available for dash. | +| youtube | supports videos, music, and shorts. 8K, 4K, HDR, VR, and high FPS videos. rich metadata & dubs. h264/av1/vp9 codecs. | -## Host an instance yourself -### Requirements -- Node.js 18 or above -- git +## 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. -Setup script installs all needed `npm` dependencies, but you have to install `Node.js` and `git` yourself. +you can use the main api instance ([co.wuk.sh](https://co.wuk.sh/)) in your projects. -1. Clone the repo: `git clone https://github.com/wukko/cobalt` -2. Run setup script and follow instructions: `npm run setup` -3. Run cobalt via `npm start` -4. Done. +## 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). +it's *highly* recommended to use a docker compose method unless you run for developing/debugging purposes. -You need to host API and web app separately since v.6.0. Setup script will help you with that! +## sponsors +cobalt is sponsored by [royalehosting.net](https://royalehosting.net/), all main instances are currently hosted on their network :) -### Ubuntu 22.04+ workaround -`nscd` needs to be installed and running so that the `ffmpeg-static` binary can resolve DNS ([#101](https://github.com/wukko/cobalt/issues/101#issuecomment-1494822258)): +## ethics and disclaimer +cobalt is a tool for easing content downloads from internet and takes ***zero liability***. you are responsible for what you download, how you use and distribute that content. please be mindful when using content of others and always credit original creators. fair use and credits benefit everyone. -```bash -sudo apt install nscd -sudo service nscd start -``` +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. -### Docker -It's also possible to run cobalt via Docker. I *highly* recommend using Docker compose. -Check out the [example compose file](https://github.com/wukko/cobalt/blob/current/docker-compose.example.yml) and alter it for your needs. +cobalt is my passion project, update schedule depends solely on my free time, motivation, and mood. don't expect any consistency in update releases. -## Disclaimer -cobalt is my passion project, so update schedule depends solely on my free time, motivation, and mood. -Don't expect any consistency in that. +## cobalt licenses +cobalt code is licensed under [AGPL-3.0](https://github.com/wukko/cobalt/blob/current/LICENSE). -## License -cobalt is under [AGPL-3.0](https://github.com/wukko/cobalt/blob/current/LICENSE) license. -[Fluent Emoji](https://github.com/microsoft/fluentui-emoji) used in the project is under [MIT](https://github.com/microsoft/fluentui-emoji/blob/main/LICENSE) license. +update banners and various assets of cobalt branding included within the repo are *not* covered by the AGPL-3.0 license and cannot be used using same terms. + +## 3rd party licenses +[Fluent Emoji by Microsoft](https://github.com/microsoft/fluentui-emoji) (used in cobalt) is under [MIT](https://github.com/microsoft/fluentui-emoji/blob/main/LICENSE) license. + +[Noto Sans Mono](https://fonts.google.com/noto/specimen/Noto+Sans+Mono/) fonts (used in cobalt) are licensed under the [OFL](https://fonts.google.com/noto/specimen/Noto+Sans+Mono/about) license. + +many update banners were taken from [tenor.com](https://tenor.com/). \ No newline at end of file diff --git a/crowdin.yml b/crowdin.yml deleted file mode 100644 index ba9e2f42..00000000 --- a/crowdin.yml +++ /dev/null @@ -1,3 +0,0 @@ -files: - - source: /src/localization/languages/en.json - translation: /src/localization/languages/%two_letters_code%.json diff --git a/docs/API.md b/docs/API.md deleted file mode 100644 index 7d5fa7d4..00000000 --- a/docs/API.md +++ /dev/null @@ -1,72 +0,0 @@ -# cobalt API Documentation -This document provides info about methods and acceptable variables for all cobalt API requests.
- -``` -⚠️ Main API instance has moved to https://co.wuk.sh/ - -Make sure your projects use the correct API domain. -``` - -## POST: ``/api/json`` -Main processing endpoint.
- -Request Body Type: ``application/json``
-Response Body Type: ``application/json`` - -### Request Body Variables -| key | type | variables | default | description | -|:--------------------|:------------|:----------------------------------|:----------|:-------------------------------------------------------------------------------| -| ``url`` | ``string`` | Sharable URL encoded as URI | ``null`` | **Must** be included in every request. | -| ``vCodec`` | ``string`` | ``h264 / av1 / vp9`` | ``h264`` | Applies only to YouTube downloads. ``h264`` is recommended for phones. | -| ``vQuality`` | ``string`` | ``144 / ... / 2160 / max`` | ``720`` | ``720`` quality is recommended for phones. | -| ``aFormat`` | ``string`` | ``best / mp3 / ogg / wav / opus`` | ``mp3`` | | -| ``isAudioOnly`` | ``boolean`` | ``true / false`` | ``false`` | | -| ``isNoTTWatermark`` | ``boolean`` | ``true / false`` | ``false`` | Changes whether downloaded TikTok videos have watermarks. | -| ``isTTFullAudio`` | ``boolean`` | ``true / false`` | ``false`` | Enables download of original sound used in a TikTok video. | -| ``isAudioMuted`` | ``boolean`` | ``true / false`` | ``false`` | Disables audio track in video downloads. | -| ``dubLang`` | ``boolean`` | ``true / false`` | ``false`` | Backend uses Accept-Language for YouTube video audio tracks when ``true``. | -| ``disableMetadata`` | ``boolean`` | ``true / false`` | ``false`` | Disables file metadata when set to ``true``. | - -### Response Body Variables -| key | type | variables | -|:---------------|:-----------|:--------------------------------------------------------------| -| ``status`` | ``string`` | ``error / redirect / stream / success / rate-limit / picker`` | -| ``text`` | ``string`` | Text | -| ``url`` | ``string`` | Direct link to a file / link to cobalt's live render | -| ``pickerType`` | ``string`` | ``various / images`` | -| ``picker`` | ``array`` | Array of picker items | -| ``audio`` | ``string`` | Direct link to a file / link to cobalt's live render | - -### Picker Item Variables -Item type: ``object`` -| key | type | variables | description | -|:---------------|:-----------|:------------------------------------------------|:--------------------------------------------| -| ``type`` | ``string`` | ``video`` | Used only if ``pickerType`` is ``various``. | -| ``url`` | ``string`` | Direct link to a file / link to cobalt's live render | | -| ``thumb`` | ``string`` | Item thumbnail that's displayed in the picker | Used only for ``video`` type. | - -## GET: ``/api/stream`` -Content live render streaming endpoint.
- -### Request Query Variables -| key | variables | description | -|:--------|:-----------------|:-------------------------------------------------------------------------------------------------------------------------------| -| ``p`` | ``1`` | Used for probing whether user is rate limited. | -| ``t`` | Stream token | Unique stream ID. Used for retrieving cached stream info data. | -| ``h`` | HMAC | Hashed combination of: (hashed) ip address, stream token, expiry timestamp, and service name. Used for verification of stream. | -| ``e`` | Expiry timestamp | | - -## GET: ``/api/serverInfo`` -Returns current basic server info.
-Response Body Type: ``application/json`` - -### Response Body Variables -| key | type | variables | -|:--------------|:-----------|:------------------| -| ``version`` | ``string`` | cobalt version | -| ``commit`` | ``string`` | Git commit | -| ``branch`` | ``string`` | Git branch | -| ``name`` | ``string`` | Server name | -| ``url`` | ``string`` | Server url | -| ``cors`` | ``int`` | CORS status | -| ``startTime`` | ``string`` | Server start time | diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 00000000..6d8cc697 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,80 @@ +# cobalt api documentation +this document provides info about methods and acceptable variables for all cobalt api requests. + +``` +👍 you can use co.wuk.sh instance in your projects for free, just don't be an asshole. +``` + +## POST: `/api/json` +cobalt's main processing endpoint. + +request body type: `application/json` +response body type: `application/json` + +``` +⚠️ you must include Accept and Content-Type headers with every POST /api/json request. + +Accept: application/json +Content-Type: application/json +``` + +### request body variables +| key | type | variables | default | description | +|:------------------|:----------|:-----------------------------------|:----------|:--------------------------------------------------------------------------------| +| `url` | `string` | URL encoded as URI | `null` | **must** be included in every request. | +| `vCodec` | `string` | `h264 / av1 / vp9` | `h264` | applies only to youtube downloads. `h264` is recommended for phones. | +| `vQuality` | `string` | `144 / ... / 2160 / max` | `720` | `720` quality is recommended for phones. | +| `aFormat` | `string` | `best / mp3 / ogg / wav / opus` | `mp3` | | +| `filenamePattern` | `string` | `classic / pretty / basic / nerdy` | `classic` | changes the way files are named. previews can be seen in the web app. | +| `isAudioOnly` | `boolean` | `true / false` | `false` | | +| `isNoTTWatermark` | `boolean` | `true / false` | `false` | changes whether downloaded tiktok videos have watermarks. | +| `isTTFullAudio` | `boolean` | `true / false` | `false` | enables download of original sound used in a tiktok video. | +| `isAudioMuted` | `boolean` | `true / false` | `false` | disables audio track in video downloads. | +| `dubLang` | `boolean` | `true / false` | `false` | backend uses Accept-Language header for youtube video audio tracks when `true`. | +| `disableMetadata` | `boolean` | `true / false` | `false` | disables file metadata when set to `true`. | +| `twitterGif` | `boolean` | `true / false` | `false` | changes whether twitter gifs are converted to .gif | + +### response body variables +| key | type | variables | +|:-------------|:---------|:------------------------------------------------------------| +| `status` | `string` | `error / redirect / stream / success / rate-limit / picker` | +| `text` | `string` | various text, mostly used for errors | +| `url` | `string` | direct link to a file or a link to cobalt's live render | +| `pickerType` | `string` | `various / images` | +| `picker` | `array` | array of picker items | +| `audio` | `string` | direct link to a file or a link to cobalt's live render | + +### picker item variables +item type: `object` + +| key | type | variables | description | +|:--------|:---------|:--------------------------------------------------------|:---------------------------------------| +| `type` | `string` | `video` | used only if `pickerType`is `various`. | +| `url` | `string` | direct link to a file or a link to cobalt's live render | | +| `thumb` | `string` | item thumbnail that's displayed in the picker | used only for `video` type. | + +## GET: `/api/stream` +cobalt's live render (or stream) endpoint. used for sending various media content over to the user. + +### request query variables +| key | variables | description | +|:-----|:-----------------|:-------------------------------------------------------------------------------------------------------------------------------| +| `p` | `1` | used for probing whether user is rate limited. | +| `t` | stream token | unique stream id. used for retrieving cached stream info data. | +| `h` | hmac | hashed combination of: (hashed) ip address, stream token, expiry timestamp, and service name. used for verification of stream. | +| `e` | expiry timestamp | | + +## GET: `/api/serverInfo` +returns current basic server info. +response body type: `application/json` + +### response body variables +| key | type | variables | +|:------------|:---------|:------------------| +| `version` | `string` | cobalt version | +| `commit` | `string` | git commit | +| `branch` | `string` | git branch | +| `name` | `string` | server name | +| `url` | `string` | server url | +| `cors` | `int` | cors status | +| `startTime` | `string` | server start time | diff --git a/src/modules/processing/cookie/cookies_example.json b/docs/examples/cookies.example.json similarity index 100% rename from src/modules/processing/cookie/cookies_example.json rename to docs/examples/cookies.example.json diff --git a/docker-compose.example.yml b/docs/examples/docker-compose.example.yml similarity index 87% rename from docker-compose.example.yml rename to docs/examples/docker-compose.example.yml index a74a89af..b5ce8a30 100644 --- a/docker-compose.example.yml +++ b/docs/examples/docker-compose.example.yml @@ -2,7 +2,7 @@ version: '3.5' services: cobalt-api: - image: ghcr.io/wukko/cobalt:latest + image: ghcr.io/wukko/cobalt:7 restart: unless-stopped container_name: cobalt-api @@ -17,14 +17,13 @@ services: #- 127.0.0.1:9000:9000 environment: - - apiPort=9000 # replace apiURL with your instance's target url in same format - apiURL=https://co.wuk.sh/ # replace apiName with your instance's distinctive name - apiName=eu-nl # if you want to use cookies when fetching data from services, uncomment the next line #- cookiePath=/cookies.json - # see src/modules/processing/cookie/cookies_example.json for example file. + # see cookies_example.json for example file. labels: - com.centurylinklabs.watchtower.scope=cobalt @@ -33,7 +32,7 @@ services: #- ./cookies.json:/cookies.json cobalt-web: - image: ghcr.io/wukko/cobalt:latest + image: ghcr.io/wukko/cobalt:7 restart: unless-stopped container_name: cobalt-web @@ -48,7 +47,6 @@ services: #- 127.0.0.1:9001:9001 environment: - - webPort=9001 # replace webURL with your instance's target url in same format - webURL=https://cobalt.tools/ # replace apiURL with preferred api instance url @@ -63,4 +61,4 @@ services: restart: unless-stopped command: --cleanup --scope cobalt --interval 900 volumes: - - /var/run/docker.sock:/var/run/docker.sock \ No newline at end of file + - /var/run/docker.sock:/var/run/docker.sock diff --git a/docs/run-an-instance.md b/docs/run-an-instance.md new file mode 100644 index 00000000..801895dc --- /dev/null +++ b/docs/run-an-instance.md @@ -0,0 +1,49 @@ +# how to host a cobalt instance yourself +## using docker compose and package from github (recommended) +to run the cobalt docker package, you need to have `docker` and `docker-compose` installed and configured. + +if you need help with installing docker, follow *only the first step* of these tutorials by digitalocean: +- [how to install docker](https://www.digitalocean.com/community/tutorial-collections/how-to-install-and-use-docker) +- [how to install docker compose](https://www.digitalocean.com/community/tutorial-collections/how-to-install-docker-compose) + +## how to run a cobalt docker package: +1. create a folder for cobalt config file, something like this: + ```sh + mkdir cobalt + ``` + +2. go to cobalt folder, and create a docker compose config file: + ```sh + cd cobalt && nano docker-compose.yml + ``` + 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. + make sure to replace default URLs with your own or cobalt won't work correctly. + +4. finally, start the cobalt container (from cobalt directory): + ```sh + 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). + +cobalt package will update automatically thanks to watchtower. + +it's highly recommended to use a reverse proxy (such as nginx) if you want your instance to face the public internet. look up tutorials online. + +## using regular node.js (useful for local development) +setup script installs all needed `npm` dependencies, but you have to install `node.js` *(version 18 or above)* and `git` yourself. + +1. clone the repo: `git clone https://github.com/wukko/cobalt`. +2. run setup script and follow instructions: `npm run setup`. you need to host api and web instances separately, so pick whichever applies. +3. run cobalt via `npm start`. +4. done. + +### ubuntu 22.04 workaround +`nscd` needs to be installed and running so that the `ffmpeg-static` binary can resolve DNS ([#101](https://github.com/wukko/cobalt/issues/101#issuecomment-1494822258)): + +```bash +sudo apt install nscd +sudo service nscd start +``` diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 00000000..8241ef98 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,33 @@ +# self-troubleshooting cobalt +``` +🚧 this page is work-in-progress. expect more guides to be added in the future! +``` +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 +you can fix this issue by changing a single preference in `about:config`. + +### steps to enable clipboard functionality +1. go to `about:config`: + + ![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". + ⚠ 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) + +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) + +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) + +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) + +6. go back to cobalt, reload the page, press `paste and download` button again. this time it works! enjoy simpler downloading experience :) diff --git a/jsconfig.json b/jsconfig.json deleted file mode 100644 index 3a840ef5..00000000 --- a/jsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "module": "ESNext", - "moduleResolution": "Node", - "target": "ES2020", - "strictNullChecks": true, - "strictFunctionTypes": true - }, - "exclude": [ - "node_modules", - "**/node_modules/*" - ] -} diff --git a/package.json b/package.json index 6b1f672b..ab8a4f0c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cobalt", "description": "save what you love", - "version": "7.5.1", + "version": "7.9.5", "author": "wukko", "exports": "./src/cobalt.js", "type": "module", @@ -12,7 +12,8 @@ "start": "node src/cobalt", "setup": "node src/modules/setup", "test": "node src/test/test", - "build": "node src/modules/buildStatic" + "build": "node src/modules/buildStatic", + "testFilenames": "node src/test/testFilenamePresets" }, "repository": { "type": "git", @@ -24,6 +25,8 @@ }, "homepage": "https://github.com/wukko/cobalt#readme", "dependencies": { + "abort-controller": "3.0.0", + "content-disposition-header": "0.6.0", "cors": "^2.8.5", "dotenv": "^16.0.1", "esbuild": "^0.14.51", @@ -33,9 +36,10 @@ "hls-parser": "^0.10.7", "nanoid": "^4.0.2", "node-cache": "^5.1.2", + "psl": "1.9.0", "set-cookie-parser": "2.6.0", "undici": "^5.19.1", "url-pattern": "1.0.3", - "youtubei.js": "^5.4.0" + "youtubei.js": "^6.4.1" } } diff --git a/src/cobalt.js b/src/cobalt.js index 949cccba..2d90e07e 100644 --- a/src/cobalt.js +++ b/src/cobalt.js @@ -21,8 +21,8 @@ app.disable('x-powered-by'); await loadLoc(); -const apiMode = process.env.apiURL && process.env.apiPort && !((process.env.webURL && process.env.webPort) || (process.env.selfURL && process.env.port)); -const webMode = process.env.webURL && process.env.webPort && !((process.env.apiURL && process.env.apiPort) || (process.env.selfURL && process.env.port)); +const apiMode = process.env.apiURL && !process.env.webURL; +const webMode = process.env.webURL && process.env.apiURL; if (apiMode) { const { runAPI } = await import('./core/api.js'); @@ -31,5 +31,9 @@ if (apiMode) { const { runWeb } = await import('./core/web.js'); await runWeb(express, app, gitCommit, gitBranch, __dirname) } else { - console.log(Red(`cobalt wasn't configured yet or configuration is invalid.\n`) + Bright(`please run the setup script to fix this: `) + Green(`npm run setup`)) + console.log( + Red(`cobalt wasn't configured yet or configuration is invalid.\n`) + + Bright(`please run the setup script to fix this: `) + + Green(`npm run setup`) + ) } diff --git a/src/config.json b/src/config.json index 10e3286d..ae0c16fc 100644 --- a/src/config.json +++ b/src/config.json @@ -1,6 +1,6 @@ { - "streamLifespan": 20000, - "maxVideoDuration": 18000000, + "streamLifespan": 90000, + "maxVideoDuration": 10800000, "genericUserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36", "authorInfo": { "name": "wukko", @@ -8,37 +8,55 @@ "contact": "https://wukko.me/contacts", "support": { "default": { + "email": { + "emoji": "📧", + "url": "mailto:support@cobalt.tools", + "name": "support@cobalt.tools" + }, "twitter": { "emoji": "🐦", "url": "https://twitter.com/justusecobalt", - "handle": "@justusecobalt" - }, - "mastodon": { - "emoji": "🐘", - "url": "https://wetdry.world/@cobalt", - "handle": "@cobalt@wetdry.world" + "name": "@justusecobalt" }, "discord": { "emoji": "👾", "url": "https://discord.gg/pQPt8HBUPu", - "handle": "cobalt community server" + "name": "cobalt discord server" + } + }, + "ru": { + "telegram": { + "emoji": "📬", + "url": "https://t.me/justusecobalt_ru", + "name": "канал в telegram" + }, + "email": { + "emoji": "📧", + "url": "mailto:support@cobalt.tools", + "name": "support@cobalt.tools" } } } }, "donations": { "crypto": { - "bitcoin": "bc1q59jyyjvrzj4c22rkk3ljeecq6jmpyscgz9spnd", - "ethereum": "0x4B4cF23051c78c7A7E0eA09d39099621c46bc302", + "monero": "4B1SNB6s8Pq1hxjNeKPEe8Qa8EP3zdL16Sqsa7QDoJcUecKQzEj9BMxWnEnTGu12doKLJBKRDUqnn6V9qfSdXpXi3Nw5Uod", "litecoin": "ltc1qvp0xhrk2m7pa6p6z844qcslfyxv4p3vf95rhna", - "monero": "4B1SNB6s8Pq1hxjNeKPEe8Qa8EP3zdL16Sqsa7QDoJcUecKQzEj9BMxWnEnTGu12doKLJBKRDUqnn6V9qfSdXpXi3Nw5Uod" + "ethereum": "0x4B4cF23051c78c7A7E0eA09d39099621c46bc302", + "usdt-erc20": "0x4B4cF23051c78c7A7E0eA09d39099621c46bc302", + "usdt-trc20": "TVbx7YT3rBfu931Gxko6pRfXtedYqbgnBB", + "bitcoin": "bc1qlvcnlnyzfsgnuxyxsv3k0p0q0yln0azjpadyx4", + "bitcoin-alt": "18PKf6N2cHrmSzz9ZzTSvDd2jAkqGC7SxA", + "ton": "UQA3SO-hHZq1oCCT--u6or6ollB8fd2o52aD8mXiLk9iDZd3" }, "links": { "boosty": "https://boosty.to/wukko/donate" } }, "links": { - "saveToGalleryShortcut": "https://www.icloud.com/shortcuts/b401917928fd407daf1db0fd07eb7e78" + "saveToGalleryShortcut": "https://www.icloud.com/shortcuts/b401917928fd407daf1db0fd07eb7e78", + "statusPage": "https://status.cobalt.tools/", + "troubleshootingGuide": "https://github.com/wukko/cobalt/blob/current/docs/troubleshooting.md" }, "celebrations": { "01-01": "🎄", @@ -73,6 +91,17 @@ "mp4": ["-c:v", "copy", "-c:a", "copy", "-movflags", "faststart+frag_keyframe+empty_moov"], "copy": ["-c:a", "copy"], "audio": ["-ar", "48000", "-ac", "2", "-b:a", "320k"], - "m4a": ["-movflags", "frag_keyframe+empty_moov"] - } + "m4a": ["-movflags", "frag_keyframe+empty_moov"], + "gif": ["-vf", "scale=-1:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse", "-loop", "0"] + }, + "sponsors": [{ + "name": "royale", + "fullName": "RoyaleHosting", + "url": "https://royalehosting.net/", + "logo": { + "width": 605, + "height": 136, + "scale": 5 + } + }] } diff --git a/src/core/api.js b/src/core/api.js index 84464b56..71ce8d3e 100644 --- a/src/core/api.js +++ b/src/core/api.js @@ -97,7 +97,7 @@ export function runAPI(express, app, gitCommit, gitBranch, __dirname) { let chck = checkJSONPost(request); if (!chck) throw new Error(); - j = await getJSON(chck["url"], lang, chck); + j = await getJSON(chck.url, lang, chck); } else { j = apiJSON(0, { t: !contentCon ? "invalid content type header" : loc(lang, 'ErrorNoLink') @@ -139,9 +139,9 @@ export function runAPI(express, app, gitCommit, gitBranch, __dirname) { version: version, commit: gitCommit, branch: gitBranch, - name: process.env.apiName ? process.env.apiName : "unknown", + name: process.env.apiName || "unknown", url: process.env.apiURL, - cors: process.env.cors && process.env.cors === "0" ? 0 : 1, + cors: process.env?.cors === "0" ? 0 : 1, startTime: `${startTimestamp}` }); default: @@ -167,12 +167,12 @@ export function runAPI(express, app, gitCommit, gitBranch, __dirname) { res.redirect('/api/json') }); - app.listen(process.env.apiPort, () => { + app.listen(process.env.apiPort || 9000, () => { console.log(`\n` + `${Cyan("cobalt")} API ${Bright(`v.${version}-${gitCommit} (${gitBranch})`)}\n` + `Start time: ${Bright(`${startTime.toUTCString()} (${startTimestamp})`)}\n\n` + `URL: ${Cyan(`${process.env.apiURL}`)}\n` + - `Port: ${process.env.apiPort}\n` + `Port: ${process.env.apiPort || 9000}\n` ) }); } diff --git a/src/core/web.js b/src/core/web.js index c2512c1f..08a6ffed 100644 --- a/src/core/web.js +++ b/src/core/web.js @@ -76,12 +76,12 @@ export async function runWeb(express, app, gitCommit, gitBranch, __dirname) { return res.redirect('/') }); - app.listen(process.env.webPort, () => { + app.listen(process.env.webPort || 9001, () => { console.log(`\n` + `${Cyan("cobalt")} WEB ${Bright(`v.${version}-${gitCommit} (${gitBranch})`)}\n` + `Start time: ${Bright(`${startTime.toUTCString()} (${startTimestamp})`)}\n\n` + `URL: ${Cyan(`${process.env.webURL}`)}\n` + - `Port: ${process.env.webPort}\n` + `Port: ${process.env.webPort || 9001}\n` ) }) } diff --git a/src/front/cobalt.css b/src/front/cobalt.css index e0e5322e..ff4f50d6 100644 --- a/src/front/cobalt.css +++ b/src/front/cobalt.css @@ -9,6 +9,7 @@ --padding-1: 0.75rem; --line-height: 1.65rem; --red: rgb(249, 47, 96); + --blue: rgb(47, 138, 249); --gap: 0.5rem; --gap-no-icon: 0.6rem; } @@ -34,13 +35,13 @@ --accent: rgb(25, 25, 25); --accent-highlight: rgb(25, 25, 25, 4%); --accent-subtext: rgb(110, 110, 110); - --accent-hover: rgb(230, 230, 230); - --accent-hover-elevated: rgb(215, 215, 215); + --accent-hover: rgb(225, 225, 225); + --accent-hover-elevated: rgb(210, 210, 210); --accent-hover-transparent: rgba(215, 215, 215, 0.5); - --accent-button: rgb(225, 225, 225); - --accent-button-elevated: rgb(210, 210, 210); - --glass: rgba(230, 230, 230, 0.85); - --glass-lite: rgba(230, 230, 230, 0.98); + --accent-button: rgb(232, 232, 232); + --accent-button-elevated: rgb(215, 215, 215); + --glass: rgba(232, 232, 232, 0.85); + --glass-lite: rgba(232, 232, 232, 0.98); --subbackground: rgb(240, 240, 240); --background: rgb(255, 255, 255); --background-backdrop: rgba(255, 255, 255, 0.5); @@ -65,13 +66,13 @@ --accent: rgb(25, 25, 25); --accent-highlight: rgb(25, 25, 25, 4%); --accent-subtext: rgb(110, 110, 110); - --accent-hover: rgb(230, 230, 230); - --accent-hover-elevated: rgb(215, 215, 215); - --accent-hover-transparent: rgba(219, 219, 219, 0.5); - --accent-button: rgb(225, 225, 225); - --accent-button-elevated: rgb(210, 210, 210); - --glass: rgba(230, 230, 230, 0.85); - --glass-lite: rgba(230, 230, 230, 0.98); + --accent-hover: rgb(225, 225, 225); + --accent-hover-elevated: rgb(210, 210, 210); + --accent-hover-transparent: rgba(215, 215, 215, 0.5); + --accent-button: rgb(232, 232, 232); + --accent-button-elevated: rgb(215, 215, 215); + --glass: rgba(232, 232, 232, 0.85); + --glass-lite: rgba(232, 232, 232, 0.98); --subbackground: rgb(240, 240, 240); --background: rgb(255, 255, 255); --background-backdrop: rgba(255, 255, 255, 0.5); @@ -106,7 +107,7 @@ a { color: var(--accent-subtext); } .switches::-webkit-scrollbar, -#popup-content::-webkit-scrollbar { +.popup-content::-webkit-scrollbar { display: none; } :focus-visible { @@ -253,19 +254,25 @@ button:active, } #cobalt-main-box { position: fixed; - width: 60%; + width: 40rem; height: auto; display: flex; - flex-direction: row; + flex-direction: column; + align-content: center; + align-items: center; } #logo { text-align: left; font-size: 1rem; white-space: nowrap; - width: 7rem; height: 2.5rem; align-items: center; display: flex; + gap: 0.3rem; +} +.logo-sub { + color: var(--blue); + font-size: 0.8rem; } #download-area { display: flex; @@ -289,7 +296,7 @@ button:active, } #url-input-area { background: none; - padding: 0 1rem; + padding-left: calc(20px + 1.4rem); width: 100%; color: var(--accent); border: 0; @@ -310,20 +317,34 @@ button:active, outline: none; border-bottom: var(--border-10); } +#link-icon { + display: flex; + position: absolute; + width: 20px; + padding-top: 0.2rem; + left: 0.7rem; + flex-wrap: nowrap; + color: var(--accent-subtext); +} #download-button { height: 2.5rem; color: var(--accent); background: none; border: none; - font-size: 1.6rem; + font-size: 1.8rem; cursor: pointer; padding: 0; - letter-spacing: -0.36rem; + letter-spacing: -0.35rem; + font-weight: normal!important; } #download-button:disabled { color: var(--accent-subtext); cursor: not-allowed; } +#cobalt-main-box .switch, +#footer .switch { + box-shadow: 0 0 0 0.1rem var(--accent-highlight) inset; +} #footer { bottom: 0; width: 100%; @@ -429,31 +450,28 @@ button:active, .popup.small.visible { transform: translate(-50%, -50%); } -.popup.small #popup-header-contents, +.popup.small .popup-header-contents, .popup.small .popup-content-inner, -.popup.small #popup-header { +.popup.small .popup-header { padding: 0; } -.popup.small #popup-header { +.popup.small .popup-header { position: relative; border: none; } -.popup.small #popup-title { +.popup.small .popup-title { margin-bottom: 0.6rem; } .popup.small .explanation { margin-bottom: 0.9rem; } -#close-error { - background: var(--accent); +.popup.small .close-error.switch { + background: var(--accent)!important; color: var(--background); } .popup.scrollable { height: 95%; } -.scrollable .bottom-link { - padding-bottom: 2rem; -} .changelog-subtitle { font-size: 1.3rem; padding-bottom: var(--gap-no-icon); @@ -502,7 +520,7 @@ button:active, font-size: 1.1rem; padding-bottom: var(--padding-1); } -#popup-desc, +.popup-desc, .desc-error, #popup-info-desc { width: 100%; @@ -515,7 +533,7 @@ button:active, .desc-error { padding-bottom: 1.5rem; } -#popup-title { +.popup-title { font-size: 1.5rem; line-height: 1.2em; display: flex; @@ -523,11 +541,11 @@ button:active, margin-bottom: 0.4rem; margin-top: 0.4rem; } -#popup-above-title { +.popup-above-title { color: var(--accent-subtext); font-size: 0.8rem; } -#popup-content { +.popup-content { overflow-x: scroll; overflow-y: auto; height: 100%; @@ -546,7 +564,7 @@ button:active, .bullpadding { padding-left: 0.58rem; } -#popup-header { +.popup-header { position: absolute; z-index: 999; padding-top: calc(env(safe-area-inset-top)/2 + 1.7rem); @@ -628,16 +646,16 @@ button:active, .switch:focus { box-shadow: var(--inset-focus) inset; } -#popup-tabs .switch { +.popup-tabs .switch { background: none; } -.desktop #popup-tabs .switch:hover, -#popup-tabs .switch:active { +.desktop .popup-tabs .switch:hover, +.popup-tabs .switch:active { background: var(--accent-hover-transparent); box-shadow: 0 0 0 0.1rem var(--accent-highlight) inset; } .switch[data-enabled="true"], -#popup-tabs .switch[data-enabled="true"] { +.popup-tabs .switch[data-enabled="true"] { color: var(--background); background: var(--accent)!important; cursor: default; @@ -675,20 +693,20 @@ button:active, padding: var(--gap-no-icon); overflow: clip; } -#back-button { +.back-button { padding: 0; background: none; max-width: 4rem; font-size: 1rem; } -#back-button svg path, +.back-button svg path, .collapse-indicator svg path { fill: var(--accent); } .popup-tab-content[data-enabled="false"] { display: none; } -#popup-tabs { +.popup-tabs { z-index: 999; bottom: 0; position: absolute; @@ -725,11 +743,13 @@ button:active, } #picker-holder { display: flex; - justify-content: space-between; + justify-content: start; flex-wrap: wrap; align-content: space-around; padding-top: calc(env(safe-area-inset-top)/2 + 7.6rem); padding-bottom: calc(env(safe-area-inset-bottom)/2 + 4.8rem); + padding-left: 0.2rem; + padding-right: 0.2rem; } .imageBlock { width: 100%; @@ -759,9 +779,6 @@ button:active, user-select: none; -webkit-user-select: none; } -.collapse-list.last { - margin-bottom: 1rem; -} .collapse-header { padding: 0.5rem var(--padding-1); font-size: 0.95rem; @@ -793,6 +810,7 @@ button:active, .collapse-body { display: none; padding: var(--padding-1); + padding-bottom: 1rem; user-select: text; -webkit-user-select: text; } @@ -805,13 +823,9 @@ button:active, #pd-share { display: none; } -#about-donate-footer { - box-shadow: 0 0 0 0.1rem var(--red) inset, 0 0 0.6rem 0 var(--red); - z-index: 1; -} .popup-content-inner, .tab-content-settings, -#popup-header-contents { +.popup-header-contents { padding-left: 1rem; padding-right: 1rem; } @@ -888,64 +902,111 @@ button:active, opacity: 1; transition: opacity 0.2s ease-out; } +.no-animation #home { + transition: none; +} +.sponsored-by-text { + text-align: center!important; + font-size: .85rem; + color: var(--accent-subtext); + user-select: none; +} +#sponsored-logos { + width: 100%; + display: flex; + justify-content: center; + flex-wrap: wrap; + gap: 0.2rem 1rem; + margin-bottom: 1rem; +} +.sponsored-logo svg { + height: inherit; + width: inherit; +} +.sponsored-logo svg path { + fill: var(--accent-subtext); +} +#filename-preview { + background: var(--accent-button); + margin-top: 0.8rem; +} +.filename-item { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + gap: 1rem; + padding: 0.5rem 0.7rem; +} +.filename-item.line { + border-bottom: 0.1rem solid var(--accent-button-elevated); +} +.filename-label { + color: var(--accent-subtext); + font-size: 0.8rem; +} +.filename-container { + overflow-wrap: anywhere; +} /* rounded corners */ #bottom #paste, #footer .switch, #audioMode, -#popup-content .switches, +.popup-content .switches, .checkbox, .changelog-img, .changelog-banner, -#close-error, +.close-error, .changelog-tag-version, #download-switcher .switch, #popup-about .switch, -#popup-tabs .switch, +.popup-tabs .switch, .text-to-copy, -.text-to-copy.text-backdrop { - border-radius: 5px / 6px; +.text-to-copy.text-backdrop, +#filename-preview { + border-radius: 6px / 7px; } [type=checkbox] { border-radius: 3px / 4px; } .popup, -.scrollable #popup-content { - border-radius: 8px / 9px; +.scrollable .popup-content { + border-radius: 8px; } -#popup-header .glass-bkg { +.popup-header .glass-bkg { border-top-left-radius: 8px 9px; border-top-right-radius: 8px 9px; border-bottom: var(--accent-highlight) solid 0.1rem; top: -1px; } -#popup-tabs .glass-bkg { +.popup-tabs .glass-bkg { border-bottom-left-radius: 8px 9px; border-bottom-right-radius: 8px 9px; border-top: var(--accent-highlight) solid 0.1rem; bottom: -1px; } -.switches .first { - border-top-left-radius: 5px 6px; - border-bottom-left-radius: 5px 6px; +.switches :first-child { + border-top-left-radius: 6px 7px; + border-bottom-left-radius: 6px 7px; } -.switches .last { - border-top-right-radius: 5px 6px; - border-bottom-right-radius: 5px 6px; +.switches :last-child { + border-top-right-radius: 6px 7px; + border-bottom-right-radius: 6px 7px; } .text-backdrop { border-radius: 3px / 4px; } -.collapse-list.first, -.collapse-list.first .collapse-header { - border-top-left-radius: 6px 7px; - border-top-right-radius: 6px 7px; +.collapse-list:first-child, +.collapse-list:first-child .collapse-header { + border-top-left-radius: 7px 8px; + border-top-right-radius: 7px 8px; } -.collapse-list.last, -.collapse-list.last .collapse-header { - border-bottom-left-radius: 6px 7px; - border-bottom-right-radius: 6px 7px; +.collapse-list:last-child, +.collapse-list:last-child .collapse-header { + border-bottom-left-radius: 7px 8px; + border-bottom-right-radius: 7px 8px; } -.collapse-list.last.expanded .collapse-header { +.collapse-list:last-child.expanded .collapse-header { border-radius: 0; } /* prevent resizing fliecker on ios if web app is installed as standalone */ @@ -964,9 +1025,6 @@ button:active, } } @media screen and (max-width: 1440px) { - #cobalt-main-box { - width: 65%; - } .popup.small { width: 30% } @@ -980,9 +1038,6 @@ button:active, } } @media screen and (max-width: 1200px) { - #cobalt-main-box { - width: 70%; - } .popup.small { width: 35% } @@ -991,9 +1046,6 @@ button:active, } } @media screen and (max-width: 1025px) { - #cobalt-main-box { - width: 75%; - } .popup.small { width: 40% } @@ -1018,14 +1070,14 @@ button:active, width: calc(100% - 1.3rem); } } -@media screen and (max-width: 720px) { +@media screen and (max-width: 660px) { #cobalt-main-box { width: calc(100% - (0.7rem * 2)); } #cobalt-main-box #bottom { - flex-direction: column-reverse; + flex-direction: row-reverse; } - #cobalt-main-box #bottom button { + #cobalt-main-box #bottom #audioMode button, #audioMode { width: 100%; } #footer { @@ -1056,12 +1108,12 @@ button:active, padding-top: calc(env(safe-area-inset-bottom)/2 + 1rem); } .popup, - #popup-header .glass-bkg, - #popup-tabs .glass-bkg, + .popup-header .glass-bkg, + .popup-tabs .glass-bkg, .glass-bkg.small { border-radius: 0; } - #popup-tabs .glass-bkg { + .popup-tabs .glass-bkg { bottom: 0; } .switches { @@ -1092,17 +1144,21 @@ button:active, } .popup.small.visible { transform: none; - transition: transform 200ms cubic-bezier(0.075, 0.82, 0.165, 1), opacity 130ms ease-in-out; + transition: transform 210ms cubic-bezier(0.062, 0.82, 0.165, 1), opacity 130ms ease-in-out; } - .popup.small #popup-header { + .popup.small .popup-header { background: none; } .no-animation .popup.small { transition: none; } - #close-error { + .close-error { bottom: 3rem; } + #picker-holder { + padding-left: 0; + padding-right: 0; + } #picker-holder::-webkit-scrollbar { display: none; } @@ -1119,22 +1175,14 @@ button:active, max-height: 100%; box-shadow: none; } - #popup-tabs { + .popup-tabs { padding-bottom: calc(env(safe-area-inset-bottom)/2 + 1.5rem); } - .bottom-link { - padding-bottom: 2rem; - } .popup-content-inner, .tab-content-settings, .popup-tabs-child, - #popup-header-contents { + .popup-header-contents { padding-left: 0.7rem; padding-right: 0.7rem; } } -@media screen and (max-width: 400px) { - .popup-title { - line-height: inherit; - } -} \ No newline at end of file diff --git a/src/front/cobalt.js b/src/front/cobalt.js index 947d7256..cdf143bc 100644 --- a/src/front/cobalt.js +++ b/src/front/cobalt.js @@ -1,4 +1,4 @@ -const version = 37; +const version = 41; const ua = navigator.userAgent.toLowerCase(); const isIOS = ua.match("iphone os"); @@ -17,7 +17,8 @@ const switchers = { "aFormat": ["mp3", "best", "ogg", "wav", "opus"], "dubLang": ["original", "auto"], "vimeoDash": ["false", "true"], - "audioMode": ["false", "true"] + "audioMode": ["false", "true"], + "filenamePattern": ["classic", "pretty", "basic", "nerdy"] }; const checkboxes = [ "alwaysVisibleButton", @@ -29,18 +30,25 @@ const checkboxes = [ "reduceTransparency", "disableAnimations", "disableMetadata", + "twitterGif", ]; const exceptions = { // used for mobile devices "vQuality": "720" }; -const bottomPopups = ["error", "download"] +const bottomPopups = ["error", "download"]; const pageQuery = new URLSearchParams(window.location.search); let store = {}; -function changeAPI(url) { - apiURL = url; +function fixApiUrl(url) { + return url.endsWith('/') ? url.slice(0, -1) : url +} + +let apiURL = fixApiUrl(defaultApiUrl); + +function changeApi(url) { + apiURL = fixApiUrl(url); return true } function eid(id) { @@ -127,6 +135,8 @@ function detectColorScheme() { document.documentElement.setAttribute("data-theme", theme); } function changeTab(evnt, tabId, tabClass) { + if (tabId === "tab-settings-other") updateFilenamePreview(); + let tabcontent = document.getElementsByClassName(`tab-content-${tabClass}`); let tablinks = document.getElementsByClassName(`tab-${tabClass}`); @@ -194,7 +204,7 @@ function popup(type, action, text) { store.isPopupOpen = true; switch (type) { case "about": - let tabId = sGet("seenAbout") ? "changelog" : "about"; + let tabId = sGet("changelogStatus") !== `${version}` ? "changelog" : "about"; if (text) tabId = text; eid(`tab-button-${type}-${tabId}`).click(); break; @@ -211,11 +221,11 @@ function popup(type, action, text) { if (navigator.canShare) eid("pd-share").style.display = "flex"; break; case "picker": + eid("picker-title").innerHTML = loc.MediaPickerTitle; + eid("picker-subtitle").innerHTML = isMobile ? loc.MediaPickerExplanationPhone : loc.MediaPickerExplanationPC; + switch (text.type) { case "images": - eid("picker-title").innerHTML = loc.ImagePickerTitle; - eid("picker-subtitle").innerHTML = isMobile ? loc.ImagePickerExplanationPhone : loc.ImagePickerExplanationPC; - eid("picker-holder").classList.remove("various"); eid("picker-download").href = text.audio; @@ -226,14 +236,11 @@ function popup(type, action, text) { `` + - `` + + `` + `` } break; default: - eid("picker-title").innerHTML = loc.MediaPickerTitle; - eid("picker-subtitle").innerHTML = isMobile ? loc.MediaPickerExplanationPhone : loc.MediaPickerExplanationPC; - eid("picker-holder").classList.add("various"); for (let i in text.arr) { @@ -243,7 +250,7 @@ function popup(type, action, text) { }>` + `
${text.arr[i].type}
` + (text.arr[i].type === 'photo' ? '' : '
') + - `` + + `` + `` } eid("picker-download").classList.remove("visible"); @@ -274,6 +281,7 @@ function changeSwitcher(li, b) { (switchers[li][i] === b) ? enable(`${li}-${b}`) : disable(`${li}-${switchers[li][i]}`) } if (li === "theme") detectColorScheme(); + if (li === "filenamePattern") updateFilenamePreview(); } else { let pref = switchers[li][0]; if (isMobile && exceptions[li]) pref = exceptions[li]; @@ -292,28 +300,6 @@ function checkbox(action) { } action === "disableChangelog" && sGet(action) === "true" ? notificationCheck("disable") : notificationCheck(); } -function loadSettings() { - if (sGet("alwaysVisibleButton") === "true") { - eid("alwaysVisibleButton").checked = true; - eid("download-button").value = '>>' - eid("download-button").style.padding = '0 1rem'; - } - if (sGet("downloadPopup") === "true" && !isIOS) { - eid("downloadPopup").checked = true; - } - if (sGet("reduceTransparency") === "true" || isOldFirefox) { - eid("cobalt-body").classList.add('no-transparency'); - } - if (sGet("disableAnimations") === "true") { - eid("cobalt-body").classList.add('no-animation'); - } - for (let i = 0; i < checkboxes.length; i++) { - if (sGet(checkboxes[i]) === "true") eid(checkboxes[i]).checked = true; - } - for (let i in switchers) { - changeSwitcher(i, sGet(i)) - } -} function changeButton(type, text) { switch (type) { case 0: //error @@ -370,8 +356,9 @@ async function download(url) { eid("url-clear").style.display = "none"; eid("url-input-area").disabled = true; let req = { - url: encodeURIComponent(url.split("&")[0].split('%')[0]), + url, aFormat: sGet("aFormat").slice(0, 4), + filenamePattern: sGet("filenamePattern"), dubLang: false } if (sGet("dubLang") === "auto") { @@ -392,6 +379,7 @@ async function download(url) { } if (sGet("disableMetadata") === "true") req.disableMetadata = true; + if (sGet("twitterGif") === "true") req.twitterGif = true; let j = await fetch(`${apiURL}/api/json`, { method: "POST", @@ -434,9 +422,13 @@ async function download(url) { let jp = await res.json(); if (jp.status === "continue") { changeDownloadButton(2, '>>>'); - if (isMobile || isSafari) { - window.location.href = j.url; - } else window.open(j.url, '_blank'); + if (sGet("downloadPopup") === "true") { + popup('download', 1, j.url) + } else { + if (isMobile || isSafari) { + window.location.href = j.url; + } else window.open(j.url, '_blank'); + } setTimeout(() => { changeButton(1) }, 2500); } else { changeButton(0, jp.text); @@ -514,6 +506,73 @@ function unpackSettings(b64) { } return changed } +function updateFilenamePreview() { + let videoFilePreview = ``; + let audioFilePreview = ``; + let resMatch = { + "max": "3840x2160", + "2160": "3840x2160", + "1440": "2560x1440", + "1080": "1920x1080", + "720": "1280x720", + "480": "854x480", + "360": "640x360", + } + // "dubLang" + // sGet("muteAudio") === "true" + switch(sGet("filenamePattern")) { + case "classic": + videoFilePreview = `youtube_yPYZpwSpKmA_${resMatch[sGet('vQuality')]}_${sGet('vCodec')}` + + `${sGet("muteAudio") === "true" ? "_mute" : ""}.${sGet('vCodec') === "vp9" ? 'webm' : 'mp4'}`; + audioFilePreview = `youtube_yPYZpwSpKmA_audio.${sGet('aFormat') !== "best" ? sGet('aFormat') : 'opus'}`; + break; + case "pretty": + videoFilePreview = + `${loc.FilenamePreviewVideoTitle} ` + + `(${sGet('vQuality') === "max" ? "2160p" : `${sGet('vQuality')}p`}, ${sGet('vCodec')}, ` + + `${sGet("muteAudio") === "true" ? "mute, " : ""}youtube).${sGet('vCodec') === "vp9" ? 'webm' : 'mp4'}`; + audioFilePreview = `${loc.FilenamePreviewAudioTitle} - ${loc.FilenamePreviewAudioAuthor} (soundcloud).${sGet('aFormat') !== "best" ? sGet('aFormat') : 'opus'}`; + break; + case "basic": + videoFilePreview = + `${loc.FilenamePreviewVideoTitle} ` + + `(${sGet('vQuality') === "max" ? "2160p" : `${sGet('vQuality')}p`}, ${sGet('vCodec')}${sGet("muteAudio") === "true" ? " mute" : ""}).${sGet('vCodec') === "vp9" ? 'webm' : 'mp4'}`; + audioFilePreview = `${loc.FilenamePreviewAudioTitle} - ${loc.FilenamePreviewAudioAuthor}.${sGet('aFormat') !== "best" ? sGet('aFormat') : 'opus'}`; + break; + case "nerdy": + videoFilePreview = + `${loc.FilenamePreviewVideoTitle} ` + + `(${sGet('vQuality') === "max" ? "2160p" : `${sGet('vQuality')}p`}, ${sGet('vCodec')}, ` + + `${sGet("muteAudio") === "true" ? "mute, " : ""}youtube, yPYZpwSpKmA).${sGet('vCodec') === "vp9" ? 'webm' : 'mp4'}`; + audioFilePreview = `${loc.FilenamePreviewAudioTitle} - ${loc.FilenamePreviewAudioAuthor} (soundcloud, 1242868615).${sGet('aFormat') !== "best" ? sGet('aFormat') : 'opus'}`; + break; + } + eid("video-filename-text").innerHTML = videoFilePreview + eid("audio-filename-text").innerHTML = audioFilePreview +} +function loadSettings() { + if (sGet("alwaysVisibleButton") === "true") { + eid("alwaysVisibleButton").checked = true; + eid("download-button").value = '>>' + eid("download-button").style.padding = '0 1rem'; + } + if (sGet("downloadPopup") === "true" && !isIOS) { + eid("downloadPopup").checked = true; + } + if (sGet("reduceTransparency") === "true" || isOldFirefox) { + eid("cobalt-body").classList.add('no-transparency'); + } + if (sGet("disableAnimations") === "true") { + eid("cobalt-body").classList.add('no-animation'); + } + for (let i = 0; i < checkboxes.length; i++) { + if (sGet(checkboxes[i]) === "true") eid(checkboxes[i]).checked = true; + } + for (let i in switchers) { + changeSwitcher(i, sGet(i)) + } + updateFilenamePreview() +} window.onload = () => { loadCelebrationsEmoji(); @@ -541,9 +600,9 @@ window.onload = () => { if (setUn !== null) { if (setUn) { sSet("migrated", "true") - eid("desc-migration").innerHTML += `

${loc.DataTransferSuccess}` + eid("desc-migration").innerHTML += `

${loc.DataTransferSuccess}` } else { - eid("desc-migration").innerHTML += `

${loc.DataTransferError}` + eid("desc-migration").innerHTML += `

${loc.DataTransferError}` } } } @@ -554,6 +613,11 @@ window.onload = () => { window.history.replaceState(null, '', window.location.pathname); notificationCheck(); + + // fix for animations not working in Safari + if (isIOS) { + document.addEventListener('touchstart', () => {}, true); + } } eid("url-input-area").addEventListener("keydown", (e) => { button(); @@ -563,9 +627,11 @@ eid("url-input-area").addEventListener("keyup", (e) => { }) document.onkeydown = (e) => { if (!store.isPopupOpen) { - if (e.ctrlKey || e.key === "/") eid("url-input-area").focus(); + if (e.metaKey || e.ctrlKey || e.key === "/") eid("url-input-area").focus(); if (e.key === "Escape" || e.key === "Clear") clearInput(); + if (e.target === eid("url-input-area")) return; + // top buttons if (e.key === "D") pasteClipboard(); if (e.key === "K") changeSwitcher('audioMode', 'false'); diff --git a/src/front/emoji/3d/film_frames.svg b/src/front/emoji/3d/film_frames.svg new file mode 100644 index 00000000..4caf8736 --- /dev/null +++ b/src/front/emoji/3d/film_frames.svg @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/front/emoji/3d/headphone.svg b/src/front/emoji/3d/headphone.svg new file mode 100644 index 00000000..90fc6cd6 --- /dev/null +++ b/src/front/emoji/3d/headphone.svg @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/front/emoji/bubbles.svg b/src/front/emoji/bubbles.svg new file mode 100644 index 00000000..e5bccc36 --- /dev/null +++ b/src/front/emoji/bubbles.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/front/emoji/email.svg b/src/front/emoji/email.svg new file mode 100644 index 00000000..144c9534 --- /dev/null +++ b/src/front/emoji/email.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/front/emoji/film_frames.svg b/src/front/emoji/film_frames.svg new file mode 100644 index 00000000..7471d431 --- /dev/null +++ b/src/front/emoji/film_frames.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/front/emoji/headphone.svg b/src/front/emoji/headphone.svg new file mode 100644 index 00000000..1c9b6702 --- /dev/null +++ b/src/front/emoji/headphone.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/front/emoji/loudspeaker.svg b/src/front/emoji/loudspeaker.svg new file mode 100644 index 00000000..6acd5873 --- /dev/null +++ b/src/front/emoji/loudspeaker.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/front/emoji/mailbox.svg b/src/front/emoji/mailbox.svg new file mode 100644 index 00000000..5dfd70b5 --- /dev/null +++ b/src/front/emoji/mailbox.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/front/emoji/wrench.svg b/src/front/emoji/wrench.svg new file mode 100644 index 00000000..b186d3b3 --- /dev/null +++ b/src/front/emoji/wrench.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/front/robots.txt b/src/front/robots.txt index 43eb659b..a5218222 100644 --- a/src/front/robots.txt +++ b/src/front/robots.txt @@ -1,2 +1,8 @@ User-Agent: * -Disallow: /icons/ /fonts/ *.js *.css \ No newline at end of file +Disallow: /emoji/ +Disallow: /fonts/ +Disallow: /icons/ +Disallow: /sponsors/ +Disallow: /updateBanners/ +Disallow: /*.js +Disallow: /*.css diff --git a/src/front/sponsors/royale.svg b/src/front/sponsors/royale.svg new file mode 100644 index 00000000..c0338038 --- /dev/null +++ b/src/front/sponsors/royale.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/front/updateBanners/catroomba.webp b/src/front/updateBanners/catroomba.webp new file mode 100644 index 00000000..a94eeeb0 Binary files /dev/null and b/src/front/updateBanners/catroomba.webp differ diff --git a/src/front/updateBanners/meowthball.webp b/src/front/updateBanners/meowthball.webp new file mode 100644 index 00000000..20a4e150 Binary files /dev/null and b/src/front/updateBanners/meowthball.webp differ diff --git a/src/front/updateBanners/meowthcenter.webp b/src/front/updateBanners/meowthcenter.webp new file mode 100644 index 00000000..8690882b Binary files /dev/null and b/src/front/updateBanners/meowthcenter.webp differ diff --git a/src/front/updateBanners/meowthpolishegg.webp b/src/front/updateBanners/meowthpolishegg.webp new file mode 100644 index 00000000..e1eff23d Binary files /dev/null and b/src/front/updateBanners/meowthpolishegg.webp differ diff --git a/src/front/updateBanners/shutup.png b/src/front/updateBanners/shutup.png deleted file mode 100644 index cbc80ae8..00000000 Binary files a/src/front/updateBanners/shutup.png and /dev/null differ diff --git a/src/front/updateBanners/shutup.webp b/src/front/updateBanners/shutup.webp new file mode 100644 index 00000000..e0a08a7d Binary files /dev/null and b/src/front/updateBanners/shutup.webp differ diff --git a/src/localization/languages/en.json b/src/localization/languages/en.json index a0dc8f60..233e9bbe 100644 --- a/src/localization/languages/en.json +++ b/src/localization/languages/en.json @@ -1,14 +1,14 @@ { "name": "english", "substrings": { - "ContactLink": "create an issue on github" + "ContactLink": "check the status page or create an issue on github." }, "strings": { "AppTitleCobalt": "cobalt", "LinkInput": "paste the link here", "AboutSummary": "cobalt is your go-to place for downloads from social and media platforms. zero ads, trackers, or other creepy bullshit. simply paste a share link and you're ready to rock!", "EmbedBriefDescription": "save what you love. no ads, trackers, or other creepy bullshit.", - "MadeWithLove": "made with <3 by wukko", + "MadeWithLove": "made with <3 by wukko", "AccessibilityInputArea": "link input area", "AccessibilityOpenAbout": "open about popup", "AccessibilityDownloadButton": "download button", @@ -45,7 +45,6 @@ "SettingsEnableDownloadPopup": "ask how to save", "AccessibilityEnableDownloadPopup": "ask what to do with downloads", "SettingsQualityDescription": "if selected quality isn't available, closest one is used instead.", - "LinkGitHubChanges": ">> see previous commits and contribute on github", "NoScriptMessage": "cobalt uses javascript for api requests and interactive interface. you have to allow javascript to use this site. there are no pesty scripts, pinky promise.", "DownloadPopupDescriptionIOS": "easiest way to save videos on ios:\n1. add this siri shortcut.\n2. press \"share\" above and select \"save to photos\" in appeared share sheet.\nif asked, review the permission request, and press \"always allow\".\n\nalternative method:\npress and hold the download button, hide the video preview, and select \"download linked file\" to download.\nthen, open safari downloads, select the file you downloaded, open share menu, and finally press \"save video\".", "DownloadPopupDescription": "download button opens a new tab with requested file. you can disable this popup in settings.", @@ -77,12 +76,12 @@ "ImagePickerExplanationPhone": "press and hold an image to save it.", "ErrorNoUrlReturned": "i didn't get a download link from the server. this should never happen. try again, but if it still doesn't work, {ContactLink}.", "ErrorUnknownStatus": "i received a response i can't process. this should never happen. try again, but if it still doesn't work, {ContactLink}.", - "PasteFromClipboard": "paste and download", + "PasteFromClipboard": "paste", "ChangelogOlder": "previous versions", "ChangelogPressToExpand": "expand", "Miscellaneous": "miscellaneous", - "ModeToggleAuto": "auto mode", - "ModeToggleAudio": "audio mode", + "ModeToggleAuto": "auto", + "ModeToggleAudio": "audio", "SettingsDisableNotifications": "hide notifications", "MediaPickerTitle": "pick what to save", "MediaPickerExplanationPC": "click or right click to download what you want.", @@ -102,14 +101,13 @@ "CollapseSupport": "support & source code", "CollapsePrivacy": "privacy policy", "ServicesNote": "this list is not final and keeps expanding over time, make sure to check it once in a while!", - "FollowSupport": "keep in touch with cobalt for support, polls, news, and more:", - "SupportNote": "please note that response may take a while, there's only one person managing everything.", - "SourceCode": "report issues, explore source code, star or fork the repo:", - "PrivacyPolicy": "cobalt's privacy policy is simple: no data about you is ever collected or stored. zero, zilch, nada, nothing.\nwhat you download is solely your business, not mine or anyone else's.\n\nif your download requires live render, some non-backtraceable data is temporarily stored in server's RAM. it's necessary for this feature to function.\n\nin this case info about requested content is stored for 20 seconds and then permanently removed.\nno one (even me) has access to this data. official cobalt codebase doesn't provide a way to read it outside of processing functions.\n\nyou can check cobalt's source code yourself and see that everything is as stated.", + "FollowSupport": "keep in touch with cobalt for news, support, and more:", + "SourceCode": "explore source code, report issues, star or fork the repo:", + "PrivacyPolicy": "cobalt's privacy policy is simple: no data about you is ever collected or stored. zero, zilch, nada, nothing.\nwhat you download is solely your business, not mine or anyone else's.\n\nif your download requires live render, some non-backtraceable data is temporarily stored in server's RAM. it's necessary for this feature to function.\n\nin this case info about requested content is stored for 90 seconds and then permanently removed.\nno one (even me) has access to this data. official cobalt codebase doesn't provide a way to read it outside of processing functions.\n\nyou can check cobalt's source code yourself and see that everything is as stated.", "ErrorYTUnavailable": "this youtube video is unavailable, it could be region or age restricted. try another one!", - "ErrorYTTryOtherCodec": "i couldn't find anything to download with your settings. try another codec or quality!\n\nnote: youtube api sometimes acts unexpectedly. blame google for this, not me.", + "ErrorYTTryOtherCodec": "i couldn't find anything to download with your settings. try another codec or quality!\n\nsometimes youtube api sometimes acts unexpectedly. try again or try another settings.", "SettingsCodecSubtitle": "youtube codec", - "SettingsCodecDescription": "h264: generally better player support, but quality tops out at 1080p.\nav1: low player support, but supports 8k & HDR.\nvp9: usually highest bitrate, preserves most detail. supports 4k & HDR.\n\npick h264 if you want best editor/player/social media compatibility.", + "SettingsCodecDescription": "h264: generally better player support, but quality tops out at 1080p.\nav1: poor player support, but supports 8k & HDR.\nvp9: usually highest bitrate, preserves most detail. supports 4k & HDR.\n\npick h264 if you want best editor/player/social media compatibility.", "SettingsAudioDub": "youtube audio track", "SettingsAudioDubDescription": "defines which audio track will be used. if dubbed track isn't available, original video language is used instead.\n\noriginal: original video language is used.\nauto: default browser (and cobalt) language is used.", "SettingsDubDefault": "original", @@ -119,31 +117,44 @@ "ShareURL": "share", "ErrorTweetUnavailable": "couldn't find anything about this tweet. this could be because its visibility is limited. try another one!", "ErrorTwitterRIP": "twitter has restricted access to any content to unauthenticated users. while there's a way to get regular tweets, spaces are, unfortunately, impossible to get at this time. i am looking into possible solutions.", - "UrgentDonate": "cobalt needs your help!", "PopupCloseDone": "done", "Accessibility": "accessibility", "SettingsReduceTransparency": "reduce transparency", "SettingsDisableAnimations": "disable animations", "FeatureErrorGeneric": "your browser doesn't allow or support this feature. check if there are any updates available and try again!", - "ClipboardErrorFirefox": "you're using firefox where all clipboard reading functionality is disabled.\n\nyou can fix this by following steps listed here!\n\n...or you can paste the link manually instead.", + "ClipboardErrorFirefox": "you're using firefox where all clipboard reading functionality is disabled.\n\nyou can fix this by following steps listed here!\n\n...or you can paste the link manually instead.", "ClipboardErrorNoPermission": "cobalt can't access the most recent item in your clipboard without your permission.\n\nif you don't want to give access, just paste the link manually instead.\n\nif you do, go to site settings and enable the clipboard permission.", - "SupportSelfTroubleshooting": "experiencing issues? try self-troubleshooting guide first!", + "SupportSelfTroubleshooting": "experiencing issues? try one of these first:", "AccessibilityGoBack": "go back and close the popup", "CollapseKeyboard": "keyboard shortcuts", "KeyboardShortcutsIntro": "use cobalt even faster with keyboard shortcuts:", "KeyboardShortcutQuickPaste": "paste the link", "KeyboardShortcutClear": "clear link input area", "KeyboardShortcutClosePopup": "close all popups", - "CollapseLegal": "legal stuff", - "FairUse": "cobalt is a tool for easing content downloads from internet and takes zero liability. you are responsible for what you download, how you use and distribute that content.\n\ncobalt does not log any info about you, it's impossible for me to snitch on you, but please be mindful when using content of others and always credit original creators!\n\nwhen used in education purposes (lecture, homework, etc) please attach the source link.\n\nfair use and credits benefit everyone.", - "UrgentFeatureUpdate71": "more supported services!", - "UrgentThanks": "thank you for support!", + "CollapseLegal": "terms and ethics", + "FairUse": "cobalt is a web tool that makes it easier to download content from the internet and takes zero liability. processing servers work like limited proxies, so no media content is ever cached or stored.\n\nyou (end user) are responsible for what you download, how you use and distribute that content. please be mindful when using content of others and always credit original creators.\n\nwhen used in education purposes (lecture, homework, etc) please attach the source link.\n\nfair use and credits benefit everyone.", "SettingsDisableMetadata": "don't add metadata", - "UrgentNewDomain": "new domain, same cobalt", "NewDomainWelcomeTitle": "hey there!", "NewDomainWelcome": "cobalt is moving! same features, same owner, simply a more rememberable domain. and still no ads.\n\ncobalt.tools is the new main domain, aka where you are now. make sure to update your bookmarks and reinstall the web app!", "DataTransferSuccess": "btw, your settings have been transferred automatically :)", "DataTransferError": "something went wrong when transferring your preferences. you'll have to open settings and configure cobalt by hand.", - "SupportNotAffiliated": "cobalt is not affiliated with any services listed above." + "SupportNotAffiliated": "cobalt is not affiliated with any services listed above.", + "SponsoredBy": "sponsored by", + "FilenameTitle": "file name style", + "FilenamePatternClassic": "classic", + "FilenamePatternPretty": "pretty", + "FilenamePatternBasic": "basic", + "FilenamePatternNerdy": "nerdy", + "FilenameDescription": "classic: default cobalt file name pattern.\nbasic: title and basic info in brackets.\npretty: title and info in brackets.\nnerdy: title and all info in brackets.\n\nsome services don’t support rich file names and always use the classic style.", + "Preview": "preview", + "FilenamePreviewVideoTitle": "Video Title", + "FilenamePreviewAudioTitle": "Audio Title", + "FilenamePreviewAudioAuthor": "Audio Author", + "StatusPage": "service status page", + "TroubleshootingGuide": "self-troubleshooting guide", + "DonateImageDescription": "cat sleeping on a laptop keyboard and typing letters repeatedly", + "SettingsTwitterGif": "convert gifs to .gif", + "SettingsTwitterGifDescription": "converting looping videos to .gif reduces quality and majorly increases file size. if you want best efficiency, keep this setting off.", + "UpdateTwitterGif": "twitter gifs and pinterest" } } diff --git a/src/localization/languages/ru.json b/src/localization/languages/ru.json index ef9ddc43..61a58ea2 100644 --- a/src/localization/languages/ru.json +++ b/src/localization/languages/ru.json @@ -1,14 +1,14 @@ { "name": "русский", "substrings": { - "ContactLink": "напиши об этом на github (можно на русском)" + "ContactLink": "глянь статус серверов или напиши о проблеме на github (можно на русском)" }, "strings": { "AppTitleCobalt": "кобальт", "LinkInput": "вставь ссылку сюда", "AboutSummary": "кобальт - твой друг при скачивании контента из соцсетей и других сервисов. никакой рекламы, трекеров и прочего мусора. вставляешь ссылку и получаешь файл. всё. ничего лишнего.", "EmbedBriefDescription": "сохраняй то, что любишь. без рекламы, трекеров и лишней мороки.", - "MadeWithLove": "сделано wukko, с <3", + "MadeWithLove": "сделано с любовью <3", "AccessibilityInputArea": "зона вставки ссылки", "AccessibilityOpenAbout": "открыть окно с инфой", "AccessibilityDownloadButton": "кнопка скачивания", @@ -45,7 +45,6 @@ "SettingsEnableDownloadPopup": "выбор метода скачивания", "AccessibilityEnableDownloadPopup": "спрашивать, что делать с загрузками", "SettingsQualityDescription": "если выбранное качество недоступно, то выбирается ближайшее к нему.", - "LinkGitHubChanges": ">> смотри предыдущие изменения на github", "NoScriptMessage": "кобальт использует javascript для обработки ссылок и интерактивного интерфейса. ты должен разрешить использование javascript, чтобы пользоваться сайтом. тут нет никаких зловредных скриптов, обещаю.", "DownloadPopupDescriptionIOS": "наиболее простой метод скачивания видео на ios:\n1. добавь этот сценарий siri.\n2. нажми \"поделиться\" выше и выбери \"save to photos\" в открывшемся окне.\nесли появляется окно с запросом разрешения, то прочитай его, потом нажми \"всегда разрешать\".\n\nальтернативный метод:\nзажми кнопку \"скачать\", затем скрой превью и выбери \"загрузить файл по ссылке\" в появившемся окне.\nпотом открой загрузки в safari, выбери скачанный файл, нажми иконку \"поделиться\", и, наконец, нажми \"сохранить видео\".", "DownloadPopupDescription": "кнопка скачивания открывает новое окно с файлом. ты можешь отключить выбор метода скачивания файла в настройках.", @@ -77,12 +76,12 @@ "ImagePickerExplanationPhone": "зажми и удерживай картинку, чтобы её сохранить.", "ErrorNoUrlReturned": "я не получил ссылку для скачивания от сервера. такого происходить не должно. попробуй ещё раз, а если не поможет, то {ContactLink}.", "ErrorUnknownStatus": "сервер ответил мне чем-то непонятным. такого происходить не должно. попробуй ещё раз, а если не поможет, то {ContactLink}.", - "PasteFromClipboard": "вставить и скачать", + "PasteFromClipboard": "вставить", "ChangelogOlder": "предыдущие версии (тоже на английском)", "ChangelogPressToExpand": "раскрыть", "Miscellaneous": "разное", - "ModeToggleAuto": "авто режим", - "ModeToggleAudio": "аудио режим", + "ModeToggleAuto": "авто", + "ModeToggleAudio": "аудио", "SettingsDisableNotifications": "cкрыть уведомления", "MediaPickerTitle": "выбери, что сохранить", "MediaPickerExplanationPC": "кликни то, что хочешь скачать. также можно скачать правой кнопки мыши.", @@ -103,10 +102,9 @@ "CollapseSupport": "поддержка и исходный код", "CollapsePrivacy": "политика конфиденциальности", "ServicesNote": "этот список далеко не финальный и постоянно пополняется, заглядывай сюда почаще!", - "FollowSupport": "подписывайся на соц.сети кобальта для новостей, поддержки, участия в опросах, и многого другого:", - "SupportNote": "так как я занимаюсь разработкой и поддержкой в одиночку, время ожидания ответа может достигать нескольких часов. но я отвечаю всем, так что не стесняйся.", - "SourceCode": "пиши о проблемах, шарься в исходнике, или же форкай репозиторий:", - "PrivacyPolicy": "политика конфиденциальности кобальта довольно проста: никакие данные о тебе никогда не собираются и не хранятся. нуль, ноль, нада, ничего.\nто, что ты скачиваешь, - твоё личное дело, а не чьё-либо ещё.\n\nесли твоей загрузке требуется лайв рендер, то некоторые неотслеживаемые данные временно держатся в ОЗУ сервера. это необходимо для работы данной функции.\n\nв этом случае данные о запрошенном контенте хранятся в течение 20 секунд. по истечении этого времени всё стирается. ни у кого (даже у меня) нет доступа к временно хранящимся данным, так как официальная кодовая база кобальта не предусматривает возможности их чтения вне функций обработки.\n\nты всегда можешь посмотреть исходный код кобальта и убедиться, что всё так, как заявлено.", + "FollowSupport": "подписывайся на соц.сети кобальта для новостей и поддержки:", + "SourceCode": "шарься в исходнике, пиши о проблемах, или же форкай репозиторий:", + "PrivacyPolicy": "политика конфиденциальности кобальта довольно проста: никакие данные о тебе никогда не собираются и не хранятся. нуль, ноль, нада, ничего.\nто, что ты скачиваешь, - твоё личное дело, а не чьё-либо ещё.\n\nесли твоей загрузке требуется лайв рендер, то некоторые неотслеживаемые данные временно держатся в ОЗУ сервера. это необходимо для работы данной функции.\n\nв этом случае данные о запрошенном контенте хранятся в течение 90 секунд. по истечении этого времени всё стирается. ни у кого (даже у меня) нет доступа к временно хранящимся данным, так как официальная кодовая база кобальта не предусматривает возможности их чтения вне функций обработки.\n\nты всегда можешь посмотреть исходный код кобальта и убедиться, что всё так, как заявлено.", "ErrorYTUnavailable": "это видео недоступно, возможно оно ограничено по региону или доступу. попробуй другое!", "ErrorYTTryOtherCodec": "я не нашёл того, что мог бы скачать с твоими настройками. попробуй другой кодек или качество!", "SettingsCodecSubtitle": "кодек для видео с youtube", @@ -120,32 +118,45 @@ "ShareURL": "поделиться", "ErrorTweetUnavailable": "не смог найти что-либо об этом твите. возможно его видимость была ограничена. попробуй другой!", "ErrorTwitterRIP": "твиттер ограничил доступ к любому контенту на сайте для пользователей без аккаунтов. я нашёл лазейку, чтобы доставать обычные твиты, а для spaces, к сожалению, нет. я ищу возможные варианты выхода из ситуации.", - "UrgentDonate": "нужна твоя помощь!", "PopupCloseDone": "готово", "Accessibility": "общедоступность", "SettingsReduceTransparency": "уменьшить прозрачность", "SettingsDisableAnimations": "убрать анимации", "FeatureErrorGeneric": "твой браузер не разрешает или не поддерживает эту функцию. проверь наличие обновлений и попробуй ещё раз!", - "ClipboardErrorFirefox": "ты используешь firefox в котором все функции чтения из буфера обмена отключены по умолчанию.\n\nно это можно исправить следуя шагам, описанным здесь\n\n...или же ты можешь просто вставить ссылку вручную.", + "ClipboardErrorFirefox": "ты используешь firefox в котором все функции чтения из буфера обмена отключены по умолчанию.\n\nно это можно исправить следуя шагам, описанным здесь\n\n...или же ты можешь просто вставить ссылку вручную.", "ClipboardErrorNoPermission": "кобальт не может прочитать последний элемент в буфере обмена без твоего разрешения.\n\nесли ты не хочешь давать доступ, просто вставь ссылку вручную.\n\nну а если хочешь, то открой настройки сайта и разреши доступ на чтение буфера обмена.", - "SupportSelfTroubleshooting": "возникли проблемы? попробуй сначала исправить всё сам по этому гиду!", + "SupportSelfTroubleshooting": "возникли проблемы? попробуй сначала что-то из этого:", "AccessibilityGoBack": "вернуться назад и закрыть окно", "CollapseKeyboard": "горячие клавиши", "KeyboardShortcutsIntro": "пользуйся кобальтом ещё быстрее с горячими клавишами:", "KeyboardShortcutQuickPaste": "вставить ссылку", "KeyboardShortcutClear": "очистить зону вставки ссылки", "KeyboardShortcutClosePopup": "закрыть все окна", - "CollapseLegal": "правовые штучки", - "FairUse": "кобальт - это инструмент для облегчения скачивания контента из интернета, и он не несёт никакой ответственности. ты несёшь ответственность за то, что скачиваешь, как используешь и распространяешь скачанный контент.\n\nкобальт не собирает никакой информации о тебе, и не может донести на тебя, но, пожалуйста, будь сознателен при использовании чужого контента и всегда указывай авторов!\n\nпри использовании в образовательных целях (лекции, домашние задания и т.д.), пожалуйста, прикладывай ссылку на источник.\n\nчестное использование и указание авторства выгодно всем.", - "UrgentFeatureUpdate71": "расширение поддержки сервисов!", - "UrgentThanks": "спасибо за поддержку!", + "CollapseLegal": "принципы и этика", + "FairUse": "кобальт - это веб инструмент для облегчения скачивания контента из интернета. сервера обработки работают как ограниченные прокси, так что ничего никогда не сохраняется или кэшируется.\n\nкобальт не несёт никакой ответственности, только ты (конечный пользователь) несёшь ответственность за то, что скачиваешь, как используешь и распространяешь скачанный контент. будь сознателен при использовании чужого контента и всегда указывай авторов!\n\nприкладывай ссылку на источник при использовании в образовательных целях (лекции, домашние задания и т.п.)\n\nчестное использование и указание авторства выгодно всем.", "SettingsDisableMetadata": "не добавлять метаданные", - "UrgentNewDomain": "новый домен, тот же кобальт", "NewDomainWelcomeTitle": "привет!", "NewDomainWelcome": "кобальт переезжает! те же функции, тот же владелец, просто более запоминающийся домен. по-прежнему без рекламы.\n\ncobalt.tools - новый основной домен, т.е. где ты сейчас находишься. не забудь обновить закладки и переустановить веб-приложение!", "DataTransferSuccess": "кстати, твои настройки были перенесены автоматически :)", "DataTransferError": "при переносе настроек что-то пошло не так. придётся зайти в настройки и настроить кобальт вручную.", "SupportNotAffiliated": "кобальт не аффилирован ни с одним из перечисленных выше сервисов.", - "SupportMetaNoticeRU": "деятельность meta platforms inc. (владелец instagram) запрещена на территории россии." + "SupportMetaNoticeRU": "деятельность meta platforms inc. (владелец instagram) запрещена на территории россии.", + "SponsoredBy": "спонсируется", + "FilenameTitle": "стиль названий файлов", + "FilenamePatternClassic": "классический", + "FilenamePatternPretty": "красивый", + "FilenamePatternBasic": "простой", + "FilenamePatternNerdy": "полный", + "FilenameDescription": "классический: стандартный стиль названия файлов кобальта.\nпростой: название и основная инфа в скобках.\nкрасивый: название и инфа в скобках.\nполный: название и вся инфа в скобках.\n\nнекоторые сервисы не поддерживают красивые имена файлов и всегда используют классический стиль.", + "Preview": "превью", + "FilenamePreviewVideoTitle": "Название Видео", + "FilenamePreviewAudioTitle": "Название Аудио", + "FilenamePreviewAudioAuthor": "Автор Аудио", + "StatusPage": "статус серверов", + "TroubleshootingGuide": "гайд по устранению проблем", + "DonateImageDescription": "кошка спит на клавиатуре ноутбука и многократно печатает буквы", + "SettingsTwitterGif": "конвертировать гифки в .gif", + "SettingsTwitterGifDescription": "конвертирование зацикленного видео в .gif снижает качество и значительно увеличивает размер файла. если важна максимальная эффективность, то не используй эту функцию.", + "UpdateTwitterGif": "гифки с твиттера и одноклассники" } } diff --git a/src/localization/manager.js b/src/localization/manager.js index 76b68737..2b251fe3 100644 --- a/src/localization/manager.js +++ b/src/localization/manager.js @@ -1,6 +1,6 @@ import * as fs from "fs"; import { links, repo } from "../modules/config.js"; -import loadJson from "../modules/sub/loadJSON.js"; +import { loadJSON } from "../modules/sub/loadFromFs.js"; const locPath = './src/localization/languages'; @@ -10,13 +10,18 @@ let languages = []; export async function loadLoc() { const files = await fs.promises.readdir(locPath).catch((e) => { return [] }); files.forEach(file => { - loc[file.split('.')[0]] = loadJson(`${locPath}/${file}`); + loc[file.split('.')[0]] = loadJSON(`${locPath}/${file}`); languages.push(file.split('.')[0]) }); } export function replaceBase(s) { - return s.replace(/\n/g, '
').replace(/{saveToGalleryShortcut}/g, links.saveToGalleryShortcut).replace(/{repo}/g, repo).replace(/\*;/g, "•"); + return s + .replace(/\n/g, '
') + .replace(/{saveToGalleryShortcut}/g, links.saveToGalleryShortcut) + .replace(/{repo}/g, repo) + .replace(/{statusPage}/g, links.statusPage) + .replace(/\*;/g, "•"); } export function replaceAll(lang, str, string, replacement) { let s = replaceBase(str[string]) diff --git a/src/modules/api.js b/src/modules/api.js index 92fa5374..c3549bb3 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -1,33 +1,30 @@ -import UrlPattern from "url-pattern"; +import { services } from "./config.js"; -import { services as patterns } from "./config.js"; - -import { cleanURL, apiJSON } from "./sub/utils.js"; +import { apiJSON } from "./sub/utils.js"; import { errorUnsupported } from "./sub/errors.js"; import loc from "../localization/manager.js"; import match from "./processing/match.js"; -import hostOverrides from "./processing/hostOverrides.js"; +import { getHostIfValid } from "./processing/url.js"; -export async function getJSON(originalURL, lang, obj) { +export async function getJSON(url, lang, obj) { try { - let patternMatch, url = decodeURIComponent(originalURL), - hostname = new URL(url).hostname.split('.'), - host = hostname[hostname.length - 2]; + const host = getHostIfValid(url); - if (!url.startsWith('https://')) return apiJSON(0, { t: errorUnsupported(lang) }); - - let overrides = hostOverrides(host, url); - host = overrides.host; - url = overrides.url; - - if (!(host && host.length < 20 && host in patterns && patterns[host]["enabled"])) return apiJSON(0, { t: errorUnsupported(lang) }); - - let pathToMatch = cleanURL(url, host).split(`.${patterns[host]['tld'] ? patterns[host]['tld'] : "com"}/`)[1].replace('.', ''); - for (let i in patterns[host]["patterns"]) { - patternMatch = new UrlPattern(patterns[host]["patterns"][i]).match(pathToMatch); - if (patternMatch) break + if (!host || !services[host].enabled) { + return apiJSON(0, { t: errorUnsupported(lang) }); + } + + let patternMatch; + for (const pattern of services[host].patterns) { + patternMatch = pattern.match( + url.pathname.substring(1) + url.search + ); + if (patternMatch) break; + } + + if (!patternMatch) { + return apiJSON(0, { t: errorUnsupported(lang) }); } - if (!patternMatch) return apiJSON(0, { t: errorUnsupported(lang) }); return await match(host, patternMatch, url, lang, obj) } catch (e) { diff --git a/src/modules/changelog/changelog.json b/src/modules/changelog/changelog.json index db2a0f8e..c37e796f 100644 --- a/src/modules/changelog/changelog.json +++ b/src/modules/changelog/changelog.json @@ -1,21 +1,67 @@ { "current": { + "version": "7.9", + "date": "January 17, 2024", + "title": "twitter gifs, pinterest, ok.ru, and more!", + "banner": { + "file": "meowthball.webp", + "alt": "meowth rolling on a big catnip ball", + "width": 478, + "height": 350 + }, + "content": "yes, you read that right. cobalt now lets you convert any twitter gif to an actual .gif file! (finally)\njust go to settings and enable this feature :)\n\nservice improvements:\n*; added an option to convert gifs from twitter into actual .gif format. files will be bigger and lower quality, but maybe you want that.\n*; pinterest support has been completely redone, now all videos (and even pin.it links) are supported.\n*; added support for ok.ru in case you're a russian grandma.\n*; now processing all reddit links (including old.reddit.com).\n*; instagram live vods are now supported.\n*; fixed a rare vimeo bug related to 1440p videos.\n\nother improvements:\n*; ui fade in animation is no longer present if you've disabled animations.\n*; all images now have alt descriptions.\n*; cobalt html is now biblically correct and follows the html spec.\n*; lots of cleaning up.\n\npatches since 7.8:\n*; shift+key shortcuts are now ignored if url bar is focused.\n*; longer soundcloud links are now supported, also catching more tiktok-related errors.\n*; removed mastodon from support links as that account is no longer active.\n*; added ability to download a specific video from multi media tweets and support for /mediaViewer links.\n*; fixed modal blurriness in chromium.\n*; minor html changes (road to biblically correct one).\n\nlots of long-awaited updates (especially twitter gifs), hope you enjoy them and have a great day :D" + }, + "history": [{ + "version": "7.8", + "date": "December 25, 2023", + "title": "new years clean up! bug fixes and fresh look for the home page", + "banner": { + "file": "catroomba.webp", + "alt": "a cat riding a roomba vacuum", + "width": 300, + "height": 168 + }, + "content": "merry christmas and happy new year! this update fixes several (very annoying) bugs to help you enjoy your holidays better.\n\nyou might have already noticed, but we've refreshed the home page on desktop and mobile! less space wasted, more pleasant to look at. let us know if you like it or not :D\n\nservice improvements:\n*; #264 anything that includes a period in the url should be possible to download (including instagram stories).\n*; #73 soundcloud: falling back to mp3 instead of refusing to download the song at all.\n*; #275 youtube: query parameters are parsed and handled correctly, all links should be supported, no matter where v query is located.\n*; tlds are parsed and validated correctly (e.g. \"pinterest.co.uk\" works now).\n*; fixvx.com links are now supported.\n\ninterface improvements:\n*; cleaner and more consistent home page layout.\n*; cleaned up support section in \"about\". also includes a link to the status page.\n\ninternal improvements:\n*; urls, subdomains, and tlds are properly validated.\n*; minor clean up.\n\nchanges since 7.7:\n*; made terms and ethics more descriptive.\n*; fix only affected twitter videos.\n*; fixed quick ⌘+V pasting on mac.\n*; now catching even more youtube-related errors.\n\nthis might not seem like a lot, but even smaller changes make a difference!\n\nenjoy this update and the rest of your day :D" + }, { + "version": "7.7", + "date": "December 2, 2023", + "title": "bugfixes and better downloads!", + "banner": { + "file": "meowthpolishegg.webp", + "alt": "meowth polishing a togepi egg", + "width": 640, + "height": 480 + }, + "content": "this update fixes various issues with supported services. no new features yet, but twitter fix is surely something good to have in the meantime!\n\nservice improvements:\n*; broken twitter videos are now automatically fixed by cobalt.\n*; all vimeo videos and audios should now be possible to download.\n*; vimeo: fixed short resolution displayed in \"basic\" and \"pretty\" filename styles.\n\ninterface improvements:\n*; streamables are now easier to save on ios.\n\ninternal improvements:\n*; port env variable is now not strictly necessary for cobalt to run.\n*; minor clean up.\n\nchanges since 7.6:\n*; fix for an issue related to youtube dubs.\n*; fixed a memory leak related to live renders.\n*; handling all errors related to twitter downloads.\n*; fixed support for reddit links in various languages.\n*; added rich filenames support for twitch clips.\n*; updated support and donation lists.\n\nstay tuned for future updates and have a great day :D" + }, { + "version": "7.6", + "date": "October 15, 2023", + "title": "customizable file names, instagram stories, and first cobalt sponsor!", + "banner": { + "file": "meowthcenter.webp", + "alt": "meowth plush in a datacenter wearing a hardhat, wielding a hammer", + "width": 851, + "height": 640 + }, + "content": "as many have (very) often requested, cobalt now lets you pick between several file name format styles!\ngo to settings > other and change it to whichever you like! there's a preview of each style, so you know how exactly files are gonna look like.\n\nif you liked file names the way they were before, don't worry: classic style is still the default :)\n\non a different but not any less important note: cobalt is now sponsored by royalehosting.net!\noverall service performance and stability is gonna be better, but also more content will be possible to download thanks to geniuine server locations. and yes, still no ads or trackers.\n\nthis update also includes a bunch of other changes, check them out:\n\nservice improvements:\n*; added support for instagram stories thanks to #194.\n*; fixed reddit support thanks to #221.\n*; added support for rich file names for youtube, vimeo, soundcloud, rutube, and vk.\n*; numbers and emoji no longer disappear from file name and metadata.\n*; mute and audio dub file name tags don't appear together anymore.\n*; youtube: dub file name tag doesn't appear anymore if audio track is default.\n\ninterface improvements:\n*; added a list of sponsors to about tab. if you host an instance, it's disabled by default, but can be enabled with showSponsors env variable.\n*; about button now opens about tab when no new changelog is available.\n*; fixed download button thickness on ios.\n\nyou now can reach out to cobalt via email for support! it's located in the about tab along with other socials, such as discord.\n\ni hope you enjoy this long-awaited update and have a blissful day :D" + }, { "version": "7.5", "date": "September 16, 2023", "title": "support for twitch clips and rutube!", "banner": { "file": "twitchupdate.webp", + "alt": "meowth plush staring into the camera, laptop with generic purple service in the background", "width": 851, "height": 640 }, "content": "hey! this update (finally) adds support for twitch clips and rutube, among other smaller changes.\n\nservice improvements:\n*; added support for twitch clips. no vods, they're unnecessary. just clip whatever you want to download!\n*; added support for rutube in case you ever wanted to download something russian.\n\ninterface improvements:\n*; added a note about cobalt not being affiliated with any supported services.\n*; added a note about meta (the company) in russian.\n*; better russian localization. will keep improving it to make it sound not so robotic over time.\n\nother improvements:\n*; all official servers are now using the docker package. and so should you!\n*; moved the load balancer to poland. requests should be slightly faster now.\n*; minor codebase clean up.\n\nif you're confused about the new domain, read the older changelog! just scroll lower and press \"expand\".\n\ni hope you find this update useful and have a wonderful day :)\n\nbtw, cobalt has a pretty active community server on discord. go to about > support & source code to join!" - }, - "history": [{ + }, { "version": "7.4", "date": "September 9, 2023", "title": "new domain, what's coming in future, bug fixes, and more!", "banner": { "file": "newdomain.webp", + "alt": "text: new domain, same cobalt", "width": 960, "height": 540 }, @@ -26,26 +72,29 @@ "title": "extended video length limit, metadata toggle, ui improvements, and more!", "banner": { "file": "meowthsnap.webp", + "alt": "cartoon meowth pointing paw dramatically and saying something", "width": 500, "height": 280 }, - "content": "this update gives cobalt a sharp look in chromium browsers and makes it even more useful than before. check out the full changelog below!\n\nservice improvements:\n*; increased video length limit from 3 hours to 5 hours. feel free to download lectures you need :)\n*; you can now disable file metadata in settings.\n*; fixed a bug which previously caused some downloads to end up being 0 bytes.\n\nui improvements:\n*; fixed clickable area for urgent notice (text on top).\n*; fixed blurry header in chrome.\n*; fixed blurry tab bar in chrome.\n*; fixed blurry switches in chrome.\n*; fixed weirdly rounded corners in popups.\n*; fixed 1px gap on edges of various elements in popup in chrome.\n*; fixed overscrolling in other settings tab on ios.\n*; fixed unexpected button highlight effect on phones.\n*; removed outdated fixes for tiny screens.\n\nother improvements:\n*; cobalt web & api start faster than before, additional preparation functions aren't unexpectedly run anymore.\n*; cobalt is now available as a docker package. check it out on github.\n\nthank you for being here. i hope you have a great day :D" + "content": "this update gives cobalt a sharp look in chromium browsers and makes it even more useful than before. check out the full changelog below!\n\nservice improvements:\n*; increased video length limit from 3 hours to 5 hours. feel free to download lectures you need :)\n*; you can now disable file metadata in settings.\n*; fixed a bug which previously caused some downloads to end up being 0 bytes.\n\nui improvements:\n*; fixed clickable area for urgent notice (text on top).\n*; fixed blurry header in chrome.\n*; fixed blurry tab bar in chrome.\n*; fixed blurry switches in chrome.\n*; fixed weirdly rounded corners in popups.\n*; fixed 1px gap on edges of various elements in popup in chrome.\n*; fixed overscrolling in other settings tab on ios.\n*; fixed unexpected button highlight effect on phones.\n*; removed outdated fixes for tiny screens.\n\nother improvements:\n*; cobalt web & api start faster than before, additional preparation functions aren't unexpectedly run anymore.\n*; cobalt is now available as a docker package. check it out on github.\n\nthank you for being here. i hope you have a great day :D" }, { "version": "7.1", "date": "August 20, 2023", "title": "instagram, streamable, video metadata, and more!", "banner": { "file": "meowthproductions.webp", + "alt": "meowth roaring in a fancy circle, à la MGM studios intro", "width": 640, "height": 358 }, - "content": "service improvements:\n*; extended instagram support: high quality photos, videos, reels. everything should work without any issues, enjoy! :)\n*; added support for streamable.com (thanks to #179)\n*; added video metadata to youtube videos.\n*; fixed vk video downloads.\n*; vxtwitter links are now supported.\n*; fixed support for youtube audio dubs.\n\nui improvements:\n*; fixed picker popup: it's now scrollable in all cases and clickable areas don't overlap each other.\n\nbackend improvements:\n*; cobalt will now let you know if something goes wrong during video download instead of nuking the stream.\n*; added support for cookies (thanks to #177)\n*; replaced got with undici (thanks to #182). downloads should be slightly faster and clean of garbage in headers.\n\ninternal improvements:\n*; moved host overrides into its own module.\n*; minor clean ups.\n\neven more cool stuff is coming in future updates! thank you for using cobalt :D" + "content": "service improvements:\n*; extended instagram support: high quality photos, videos, reels. everything should work without any issues, enjoy! :)\n*; added support for streamable.com (thanks to #179)\n*; added video metadata to youtube videos.\n*; fixed vk video downloads.\n*; vxtwitter links are now supported.\n*; fixed support for youtube audio dubs.\n\nui improvements:\n*; fixed picker popup: it's now scrollable in all cases and clickable areas don't overlap each other.\n\nbackend improvements:\n*; cobalt will now let you know if something goes wrong during video download instead of nuking the stream.\n*; added support for cookies (thanks to #177)\n*; replaced got with undici (thanks to #182). downloads should be slightly faster and clean of garbage in headers.\n\ninternal improvements:\n*; moved host overrides into its own module.\n*; minor clean ups.\n\neven more cool stuff is coming in future updates! thank you for using cobalt :D" }, { "version": "7.0", "date": "August 15, 2023", "title": "biggest ui refresh yet!", "banner": { "file": "meowthcooking.webp", + "alt": "meowth handling orders in a restaurant", "width": 640, "height": 360 }, @@ -56,6 +105,7 @@ "title": "all network issues have been fixed!", "banner": { "file": "meowthhammer.webp", + "alt": "meowth plush holding a hammer in real life", "width": 1280, "height": 827 }, @@ -66,15 +116,17 @@ "title": "better reliability, new infrastructure, pinterest support, and way more!", "banner": { "file": "catswitchboxes.webp", + "alt": "a cat climbing into two empty boxes of asahi beer", "width": 600, "height": 314 }, - "content": "hey! long time no see, hopefully over 40 changes will make up for it :)\n\ncobalt now has an official community discord server. you can go there for news, support, or just to chat. go check it out!\n\ntl;dr\n*; new infra, new hosting structure, new main instance api url. developers, get it here.\n*; added support for pinterest, vine archive, tumblr audio, youtube vr videos.\n*; better web app performance and look.\n*; better stability thanks to load balancing.\n*; (hopefully) no more random video/audio download drops.\n\nservice improvements:\n*; added support for pinterest videos and stories (pr by @Snazzah).\n*; added support for tumblr audio. sorry, tumblr.\n*; added support for youtube vr videos. please note that they're in youtube's proprietary ratio.\n*; added support for vine archive.\n*; added support for ancient vk videos in 240p.\n*; fixed an issue related to muted video downloads from tumblr.\n*; moved to twitter v2 api.\n*; soundcloud share links are now processed without errors.\n\nui improvements:\n*; lazy image loading. should significantly speed up the page load.\n*; fixed checkbox width on mobile devices.\n*; addition of a temporary urgent notice.\n*; added hover border to all buttons.\n*; less annoying donation button highlight.\n*; more consistent color scheme.\n*; added link to a discord server into about popup.\n*; remember celebratory emoji changes? they've been fixed, and are now dynamically loaded!\n*; changelog history now lets you try to load it again if first attempt failed for whatever reason.\n*; padding (everywhere) has been slightly reduced to fit in more content and be consistent across ui.\n*; added more info to the \"how to save\" popup for ios devices.\n*; crypto wallet press-to-copy buttons now look like buttons.\n*; improved ui layout for smallest screens (iphone 5, 5s, se, etc).\n*; removed partial translations for sake of clarity and consistency.\n\ninternal improvements:\n*; separated web and api servers. they're now completely independent and therefore more stress-resistant.\n*; added a dedicated script for building the web app if you don't want to reload the frontend server.\n*; web app building improvements.\n*; async localization preloading.\n*; consistent server start time reporting.\n*; dynamic stream and ip hashing salt generation.\n\ninfrastructure improvements:\n*; load balancing: your api requests are now sent to the least busy server. yes, there are now several of them with more to come in the future.\n*; when possible, server in closest region is used instead of a far-away one. this should help with download speeds.\n*; currently there are multiple servers in europe. i will let you know when (and if) i manage to get an american one.\n\nupdates for developers and instance hosters:\n*; server info api endpoint: you can now check up on the api server of choice. it reports all the basic info you may need. check the api docs for more info.\n*; api names: each and every api instance should have a distinctive name. this will be useful in the future :)\n*; added docker compose sample config.\n*; updated and more granular setup script.\n*; better api scalability and faster server start up thanks to web and api separation.\n*; added ability to specify ffmpeg threads. simply add ffmpegThreads to your environment variables!\n\ni'm still in awe from how popular cobalt has become. there are now over 200k of unique users monthly, and that number only keeps growing. i even had to come up with something to accommodate for larger traffic, it's absolutely insane.\n\nlove you all, have a great day :D" + "content": "hey! long time no see, hopefully over 40 changes will make up for it :)\n\ncobalt now has an official community discord server. you can go there for news, support, or just to chat. go check it out!\n\ntl;dr\n*; new infra, new hosting structure, new main instance api url. developers, get it here.\n*; added support for pinterest, vine archive, tumblr audio, youtube vr videos.\n*; better web app performance and look.\n*; better stability thanks to load balancing.\n*; (hopefully) no more random video/audio download drops.\n\nservice improvements:\n*; added support for pinterest videos and stories (pr by @Snazzah).\n*; added support for tumblr audio. sorry, tumblr.\n*; added support for youtube vr videos. please note that they're in youtube's proprietary ratio.\n*; added support for vine archive.\n*; added support for ancient vk videos in 240p.\n*; fixed an issue related to muted video downloads from tumblr.\n*; moved to twitter v2 api.\n*; soundcloud share links are now processed without errors.\n\nui improvements:\n*; lazy image loading. should significantly speed up the page load.\n*; fixed checkbox width on mobile devices.\n*; addition of a temporary urgent notice.\n*; added hover border to all buttons.\n*; less annoying donation button highlight.\n*; more consistent color scheme.\n*; added link to a discord server into about popup.\n*; remember celebratory emoji changes? they've been fixed, and are now dynamically loaded!\n*; changelog history now lets you try to load it again if first attempt failed for whatever reason.\n*; padding (everywhere) has been slightly reduced to fit in more content and be consistent across ui.\n*; added more info to the \"how to save\" popup for ios devices.\n*; crypto wallet press-to-copy buttons now look like buttons.\n*; improved ui layout for smallest screens (iphone 5, 5s, se, etc).\n*; removed partial translations for sake of clarity and consistency.\n\ninternal improvements:\n*; separated web and api servers. they're now completely independent and therefore more stress-resistant.\n*; added a dedicated script for building the web app if you don't want to reload the frontend server.\n*; web app building improvements.\n*; async localization preloading.\n*; consistent server start time reporting.\n*; dynamic stream and ip hashing salt generation.\n\ninfrastructure improvements:\n*; load balancing: your api requests are now sent to the least busy server. yes, there are now several of them with more to come in the future.\n*; when possible, server in closest region is used instead of a far-away one. this should help with download speeds.\n*; currently there are multiple servers in europe. i will let you know when (and if) i manage to get an american one.\n\nupdates for developers and instance hosters:\n*; server info api endpoint: you can now check up on the api server of choice. it reports all the basic info you may need. check the api docs for more info.\n*; api names: each and every api instance should have a distinctive name. this will be useful in the future :)\n*; added docker compose sample config.\n*; updated and more granular setup script.\n*; better api scalability and faster server start up thanks to web and api separation.\n*; added ability to specify ffmpeg threads. simply add ffmpegThreads to your environment variables!\n\ni'm still in awe from how popular cobalt has become. there are now over 200k of unique users monthly, and that number only keeps growing. i even had to come up with something to accommodate for larger traffic, it's absolutely insane.\n\nlove you all, have a great day :D" }, { "version": "5.4", "title": "instagram support, docker, and more!", "banner": { "file": "catphonestand.webp", + "alt": "a cat holding a phone under its chin while a person plays clash of clans on it", "width": 451, "height": 272 }, @@ -84,6 +136,7 @@ "title": "better looks, better feel", "banner": { "file": "cattired.webp", + "alt": "a cat laying on a sofa face down, wiggling its tail", "width": 640, "height": 286 }, @@ -93,6 +146,7 @@ "title": "fastest one in the game", "banner": { "file": "catspeed.webp", + "alt": "a cat running very fast in an exercise wheel", "width": 640, "height": 356 }, @@ -102,6 +156,7 @@ "title": "the evil has been defeated", "banner": { "file": "happymeowth.webp", + "alt": "meowth jumping up into the sky very excitedly", "width": 500, "height": 330 }, @@ -111,6 +166,7 @@ "title": "it's all about attention to detail!", "banner": { "file": "valentines.webp", + "alt": "relaxed meowth with sakura petals falling in front of them", "width": 489, "height": 374 }, @@ -120,6 +176,7 @@ "title": "prettier than ever", "banner": { "file": "catmakeup.webp", + "alt": "a cat being brushed with a powder makeup brush", "width": 394, "height": 266 }, @@ -129,6 +186,7 @@ "title": "we're better together! thank you for bug reports.", "banner": { "file": "bettertogether.webp", + "alt": "various different pokémon jumping in happiness", "width": 640, "height": 358 }, @@ -138,6 +196,7 @@ "title": "mute videos and proper soundcloud support", "banner": { "file": "shutup.webp", + "alt": "a cat yawning, with a crossed out loudspeaker icon next to it", "width": 1024, "height": 665 }, @@ -147,15 +206,17 @@ "title": "better, faster, stronger, stable", "banner": { "file": "meowthstrong.webp", + "alt": "meowth stretching", "width": 500, "height": 280 }, - "content": "your favorite social media downloader just got even better! this update includes a ton of improvements and fixes.\n\nin fact, there are so many changes, i had to split them in sections.\n\nservice-related improvements:\n*; vimeo module has been revamped, all sorts of videos should now be supported.\n*; vimeo audio downloads! you now can download audios from more recent videos.\n*; cobalt now supports all sorts of tumblr links. (even those scary ones from the mobile app)\n*; vk clips support has been fixed. they rolled back the separation of videos and clips, so i had to do the same.\n*; youtube videos with community warnings should now be possible to download.\nuser interface improvements:\n*; list of supported services is now MUCH easier to read.\n*; banners in changelog history should no longer overlap each other.\n*; bullet points! they have a bit of extra padding, so it makes them stand out of the rest of text.\ninternal improvements:\n*; cobalt will now match the link to regex when using ?u= query for autopasting it into input area.\n*; better rate limiting: limiting now is done per minute, not per 20 minutes. this ensures less waiting and less attack area for request spammers.\n*; moved to my own fork of ytdl-core, cause main project seems to have been abandoned. go check it out on github or npm!\n*; ALL user inputs are now properly sanitized on the server. that includes variables for POST api method, too.\n*; \"got\" package has been (mostly) replaced by native fetch api. this should greatly reduce ram usage.\n*; all unnecessary duplications of module imports have been gotten rid of. no more error passing strings from inside of service modules. you don't make mistakes only if you don't do anything, right?\n*; other code optimizations. there's less clutter overall.\nhuge update, right? seems like everything's fixed now?\n\nnope, one issue still persists: sometimes youtube server drops packets for an audio file while cobalt's rendering the video for you. this results in abrupt cuts of audio. if you want to help solving this issue, please feel free to do it on github!\n\nthank you for reading this, and thank you for sticking with cobalt and me." + "content": "your favorite social media downloader just got even better! this update includes a ton of improvements and fixes.\n\nin fact, there are so many changes, i had to split them in sections.\n\nservice-related improvements:\n*; vimeo module has been revamped, all sorts of videos should now be supported.\n*; vimeo audio downloads! you now can download audios from more recent videos.\n*; cobalt now supports all sorts of tumblr links. (even those scary ones from the mobile app)\n*; vk clips support has been fixed. they rolled back the separation of videos and clips, so i had to do the same.\n*; youtube videos with community warnings should now be possible to download.\nuser interface improvements:\n*; list of supported services is now MUCH easier to read.\n*; banners in changelog history should no longer overlap each other.\n*; bullet points! they have a bit of extra padding, so it makes them stand out of the rest of text.\ninternal improvements:\n*; cobalt will now match the link to regex when using ?u= query for autopasting it into input area.\n*; better rate limiting: limiting now is done per minute, not per 20 minutes. this ensures less waiting and less attack area for request spammers.\n*; moved to my own fork of ytdl-core, cause main project seems to have been abandoned. go check it out on github or npm!\n*; ALL user inputs are now properly sanitized on the server. that includes variables for POST api method, too.\n*; \"got\" package has been (mostly) replaced by native fetch api. this should greatly reduce ram usage.\n*; all unnecessary duplications of module imports have been gotten rid of. no more error passing strings from inside of service modules. you don't make mistakes only if you don't do anything, right?\n*; other code optimizations. there's less clutter overall.\nhuge update, right? seems like everything's fixed now?\n\nnope, one issue still persists: sometimes youtube server drops packets for an audio file while cobalt's rendering the video for you. this results in abrupt cuts of audio. if you want to help solving this issue, please feel free to do it on github!\n\nthank you for reading this, and thank you for sticking with cobalt and me." }, { "version": "4.4", "title": "over 1 million monthly requests. thank you.", "banner": { "file": "onemillionr.webp", + "alt": "cobalt logo and a confetti emoji", "width": 1441, "height": 1441 }, @@ -169,10 +230,11 @@ "title": "developers, developers, developers, developers", "banner": { "file": "developers.webp", + "alt": "steve ballmer going \"developers, developers, developers\"", "width": 640, "height": 360 }, - "content": "this update features a TON of improvements.\n\ndevelopers, you now can rely on cobalt for getting content from social media. the api has been revamped and documentation is now available. you can read more about API changes down below. go crazy, and have fun :D\n\nif you're not a developer, here's a list of changes that you probably care about:\n- rate limit is now approximately 8 times bigger. no more waiting, even if you want to download entirety of your tiktok \"for you\" page.\n- some updates will now have expressive banners, just like this one.\n- fixed what was causing an error when a youtube video had no description.\n- mp4 format button text should now be displayed properly, no matter if you touched the switcher or not.\n\nnext, the star of this update — improved api!\n- main endpoint now uses POST method instead of GET.\n- internal variables for preferences have been updated to be consistent and easier to understand.\n- ip address is now hashed right upon request, not somewhere deep inside the code.\n- global stream salt variable is no longer unnecessarily passed over a billion functions.\n- url and picker keys are now separate in the json response.\n- cobalt web app now correctly processes responses with \"success\" status.\n\nif you currently have a siri shortcut or some other script that uses the GET method, make sure to update it soon. this method is deprecated, limited, and will be removed entirely in coming updates.\n\nif you ever make something using cobalt's api, make sure to mention @justusecobalt on twitter, i would absolutely love to see what you made." + "content": "this update features a TON of improvements.\n\ndevelopers, you now can rely on cobalt for getting content from social media. the api has been revamped and documentation is now available. you can read more about API changes down below. go crazy, and have fun :D\n\nif you're not a developer, here's a list of changes that you probably care about:\n- rate limit is now approximately 8 times bigger. no more waiting, even if you want to download entirety of your tiktok \"for you\" page.\n- some updates will now have expressive banners, just like this one.\n- fixed what was causing an error when a youtube video had no description.\n- mp4 format button text should now be displayed properly, no matter if you touched the switcher or not.\n\nnext, the star of this update — improved api!\n- main endpoint now uses POST method instead of GET.\n- internal variables for preferences have been updated to be consistent and easier to understand.\n- ip address is now hashed right upon request, not somewhere deep inside the code.\n- global stream salt variable is no longer unnecessarily passed over a billion functions.\n- url and picker keys are now separate in the json response.\n- cobalt web app now correctly processes responses with \"success\" status.\n\nif you currently have a siri shortcut or some other script that uses the GET method, make sure to update it soon. this method is deprecated, limited, and will be removed entirely in coming updates.\n\nif you ever make something using cobalt's api, make sure to mention @justusecobalt on twitter, i would absolutely love to see what you made." }, { "version": "4.2", "title": "optimized quality picking and 8k video support", @@ -184,7 +246,7 @@ }, { "version": "4.0", "title": "better and faster than ever", - "content": "this update has a ton of improvements and new features.\n\nchanges you probably care about:\n- cobalt now has support for recorded twitter spaces! download the previous conversation no matter how long it was.\n- download speeds from youtube are at least 10 times better now. you're welcome.\n- both video and audio length limits have been extended to 2 hours.\n- audio downloads from youtube, youtube music, twitter spaces, and soundcloud now have metadata! most often it's just title and artist, but when cobalt is able to get more info, it adds that metadata too.\n- tiktok downloads have been fixed, yet again, and if they ever break in the future, cobalt will fall back to downloading a less annoyingly watermarked video.\n- soundcloud downloads have been fixed, too.\n\nless notable changes:\n- currently experimenting with using mp3 as default audio format. if you set something other than mp3 before, it'll be set to mp3. you can always change it back in settings. let me know what you think about this.\n- \"download audio\" button from image picker no longer stays on the screen after popup was closed.\n- clipboard button now shows up depending on your browser's support for it.\n- you can no longer manually hide the clipboard button, 'cause it's unnecessary.\n- small internal improvements such as separation of changelog version and title.\n- fair bit of internal clean up.\n\nif you want to help me implement covers for downloaded audios, you can do it on github." + "content": "this update has a ton of improvements and new features.\n\nchanges you probably care about:\n- cobalt now has support for recorded twitter spaces! download the previous conversation no matter how long it was.\n- download speeds from youtube are at least 10 times better now. you're welcome.\n- both video and audio length limits have been extended to 2 hours.\n- audio downloads from youtube, youtube music, twitter spaces, and soundcloud now have metadata! most often it's just title and artist, but when cobalt is able to get more info, it adds that metadata too.\n- tiktok downloads have been fixed, yet again, and if they ever break in the future, cobalt will fall back to downloading a less annoyingly watermarked video.\n- soundcloud downloads have been fixed, too.\n\nless notable changes:\n- currently experimenting with using mp3 as default audio format. if you set something other than mp3 before, it'll be set to mp3. you can always change it back in settings. let me know what you think about this.\n- \"download audio\" button from image picker no longer stays on the screen after popup was closed.\n- clipboard button now shows up depending on your browser's support for it.\n- you can no longer manually hide the clipboard button, 'cause it's unnecessary.\n- small internal improvements such as separation of changelog version and title.\n- fair bit of internal clean up.\n\nif you want to help me implement covers for downloaded audios, you can do it on github." }, { "version": "3.7", "title": "support for multi media tweets is here!", diff --git a/src/modules/changelog/changelogManager.js b/src/modules/changelog/changelogManager.js index 40ddc5a0..b8763fb5 100644 --- a/src/modules/changelog/changelogManager.js +++ b/src/modules/changelog/changelogManager.js @@ -1,37 +1,39 @@ import { replaceBase } from "../../localization/manager.js"; -import loadJSON from "../sub/loadJSON.js"; +import { loadJSON } from "../sub/loadFromFs.js"; let changelog = loadJSON('./src/modules/changelog/changelog.json') export default function(string) { try { + const currentChangelog = changelog.current; + switch (string) { case "version": - return `v.${changelog["current"]["version"]}${ - changelog["current"]["date"] ? `· ${changelog["current"]["date"]}` : '' + return `v.${currentChangelog.version}${ + currentChangelog.date ? `· ${currentChangelog.date}` : '' }` case "title": - return replaceBase(changelog["current"]["title"]); + return replaceBase(currentChangelog.title); case "banner": - return changelog["current"]["banner"] ? { - url: `updateBanners/${changelog["current"]["banner"]["file"]}`, - width: changelog["current"]["banner"]["width"], - height: changelog["current"]["banner"]["height"] + const currentBanner = changelog.current.banner; + return currentBanner ? { + ...currentBanner, + url: `updateBanners/${currentBanner.file}` } : false; case "content": - return replaceBase(changelog["current"]["content"]); + return replaceBase(currentChangelog.content); case "history": - return changelog["history"].map((i) => { + return changelog.history.map((log) => { + const banner = log.banner; return { - title: replaceBase(i["title"]), - version: `v.${i["version"]}${ - i["date"] ? `· ${i["date"]}` : '' + title: replaceBase(log.title), + version: `v.${log.version}${ + log.date ? `· ${log.date}` : '' }`, - content: replaceBase(i["content"]), - banner: i["banner"] ? { - url: `updateBanners/${i["banner"]["file"]}`, - width: i["banner"]["width"], - height: i["banner"]["height"] + content: replaceBase(log.content), + banner: banner ? { + ...banner, + url: `updateBanners/${banner.file}` } : false, } }); diff --git a/src/modules/config.js b/src/modules/config.js index 5268b8dd..5e079536 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -1,7 +1,16 @@ -import loadJson from "./sub/loadJSON.js"; -const config = loadJson("./src/config.json"); -const packageJson = loadJson("./package.json"); -const servicesConfigJson = loadJson("./src/modules/processing/servicesConfig.json"); +import UrlPattern from "url-pattern"; +import { loadJSON } from "./sub/loadFromFs.js"; +const config = loadJSON("./src/config.json"); +const packageJson = loadJSON("./package.json"); +const servicesConfigJson = loadJSON("./src/modules/processing/servicesConfig.json"); + +Object.values(servicesConfigJson.config).forEach(service => { + service.patterns = service.patterns.map( + pattern => new UrlPattern(pattern, { + segmentValueCharset: UrlPattern.defaultOptions.segmentValueCharset + '@\\.' + }) + ) +}) export const services = servicesConfigJson.config, @@ -16,4 +25,5 @@ export const ffmpegArgs = config.ffmpegArgs, supportedAudio = config.supportedAudio, celebrations = config.celebrations, - links = config.links + links = config.links, + sponsors = config.sponsors diff --git a/src/modules/emoji.js b/src/modules/emoji.js index 04e053f0..f4793cb5 100644 --- a/src/modules/emoji.js +++ b/src/modules/emoji.js @@ -35,12 +35,20 @@ const names = { "📑": "boring_document", "🧮": "abacus", "😸": "cat_grin", - "📰": "newspaper" + "📰": "newspaper", + "🎞️": "film_frames", + "🎧": "headphone", + "📧": "email", + "📬": "mailbox", + "📢": "loudspeaker", + "🔧": "wrench", + "🫧": "bubbles" } let sizing = { 18: 0.8, 22: 0.4, 30: 0.7, + 32: 0.8, 48: 0.9, 64: 0.9, 78: 0.9 @@ -54,5 +62,5 @@ export default function(emoji, size, disablePadding, fluent) { let filePath = `emoji/${names[emoji]}.svg`; if (fluent) filePath = `emoji/3d/${names[emoji]}.svg`; - return `` + return `` } diff --git a/src/modules/pageRender/elements.js b/src/modules/pageRender/elements.js index 65f96699..a677d2bc 100644 --- a/src/modules/pageRender/elements.js +++ b/src/modules/pageRender/elements.js @@ -1,5 +1,6 @@ -import { authorInfo, celebrations } from "../config.js"; +import { authorInfo, celebrations, sponsors } from "../config.js"; import emoji from "../emoji.js"; +import { loadFile } from "../sub/loadFromFs.js"; export const backButtonSVG = ` @@ -9,6 +10,8 @@ export const dropdownSVG = ` ` +export const linkSVG = '' + export function switcher(obj) { let items = ``; if (obj.name === "download") { @@ -16,8 +19,6 @@ export function switcher(obj) { } else { for (let i = 0; i < obj.items.length; i++) { let classes = obj.items[i]["classes"] ? obj.items[i]["classes"] : []; - if (i === 0) classes.push("first"); - if (i === (obj.items.length - 1)) classes.push("last"); items += `` } } @@ -58,35 +59,35 @@ export function popup(obj) { body = `` for (let i = 0; i < obj.body.length; i++) { if (obj.body[i]["text"].length > 0) { - classes = obj.body[i]["classes"] ? obj.body[i]["classes"] : [] + classes = obj.body[i]["classes"] ?? [] if (i !== obj.body.length - 1 && !obj.body[i]["nopadding"]) { classes.push("desc-padding") } - body += obj.body[i]["raw"] ? obj.body[i]["text"] : `` + body += obj.body[i]["raw"] ? obj.body[i]["text"] : `
${obj.body[i]["text"]}
` } } } return ` - ${obj.standalone ? `