Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/test-all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ on:
permissions:
contents: read

# Avoid burning the 3-OS matrix on superseded pushes/PR updates.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
NODE_VERSION: '24'

Expand Down Expand Up @@ -123,3 +128,41 @@ jobs:

- name: Dependency Review
uses: actions/dependency-review-action@v5

coverage:
name: Coverage thresholds
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm

- name: Install dependencies
run: npm ci

- name: Run coverage and enforce per-file thresholds
run: npm run test:cov

audit:
name: npm audit
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm

- name: Install dependencies
run: npm ci

- name: Audit dependencies (fails on high/critical)
run: npm audit --audit-level=high
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,6 @@ resources/ffmpeg/**/ffplay.exe

# REPL history
.node_repl_history

# Test scratch dirs
.tmp-*/
12 changes: 9 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

| <img height="20" src="https://github.com/user-attachments/assets/340d360e-79b1-4c70-bfab-d944085f75df" /> Windows | <img height="20" src="https://github.com/user-attachments/assets/42d7e887-4616-4e8c-b1d3-e44e01340f8c" /> macOS | <img height="20" src="https://github.com/user-attachments/assets/e0cc4f33-4516-408b-9c5c-be71a3ac316b" /> Linux |
| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **EXE:** [x64](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.1/ROSI-Windows-x64.exe) / [arm64](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.1/ROSI-Windows-arm64.exe) | **[Universal DMG](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.1/ROSI-MacOS-universal.dmg)** | **AppImage:** [x64](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.1/ROSI-Linux-x86_64.AppImage) / [arm64](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.1/ROSI-Linux-arm64.AppImage) |
| <div align="center"><a href="https://apps.microsoft.com/detail/9p4q134b2jw3?referrer=appbadge&mode=direct"><img src="https://get.microsoft.com/images/en-us%20dark.svg" width="150"/></a></div> | **[Universal ZIP](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.1/ROSI-MacOS-universal.zip)** | **DEB:** [x64](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.1/ROSI-Linux-amd64.deb) / [arm64](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.1/ROSI-Linux-arm64.deb) |
| | | **RPM:** [x64](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.1/ROSI-Linux-x86_64.rpm) / [arm64](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.1/ROSI-Linux-aarch64.rpm) |
| **EXE:** [x64](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.2/ROSI-Windows-x64.exe) / [arm64](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.2/ROSI-Windows-arm64.exe) | **[Universal DMG](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.2/ROSI-MacOS-universal.dmg)** | **AppImage:** [x64](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.2/ROSI-Linux-x86_64.AppImage) / [arm64](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.2/ROSI-Linux-arm64.AppImage) |
| <div align="center"><a href="https://apps.microsoft.com/detail/9p4q134b2jw3?referrer=appbadge&mode=direct"><img src="https://get.microsoft.com/images/en-us%20dark.svg" width="150"/></a></div> | **[Universal ZIP](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.2/ROSI-MacOS-universal.zip)** | **DEB:** [x64](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.2/ROSI-Linux-amd64.deb) / [arm64](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.2/ROSI-Linux-arm64.deb) |
| | | **RPM:** [x64](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.2/ROSI-Linux-x86_64.rpm) / [arm64](https://github.com/BurntToasters/ROSI/releases/download/v4.1.3-beta.2/ROSI-Linux-aarch64.rpm) |

> [!IMPORTANT]
> The `.sig` files in this repo are NOT normal GPG signatures — they are for ROSI's built-in updater to verify the integrity of updates before downloading and installing.
Expand All @@ -29,6 +29,12 @@

---

## Changes in `v4.1.3-beta.2:`

- **escapeHtml:** Fixed some issues with the html sanitizer.
- **Codebase:** Addressed multiple back-end building issues.
- **Misc:** Many bug fixes and improvements.

## Changes in `v4.1.3-beta.1:`

- **FFMPEG:** Ensure bundled ffprobe is executable and always pass the bundled helper directory to yt-dlp so metadata extraction can find it.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ ROSI is an Electron GUI for yt-dlp

# LICENSES

- Rosi includes the official YT-DLP binary which on its own uses the [unlicense] license, however there are bundled third party packages. Read [THIRDPARTYNOTICES](THIRDPARTYNOTICES.md) for more.
- Rosi includes the official YT-DLP binary which on its own uses the [unlicense] license, however there are bundled third party packages. Read [THIRD-PARTY-NOTICES](THIRD-PARTY-NOTICES.md) for more.
- Please make sure to also read the [license](LICENSE) for the source of this project (excluding third part binaries and packages).

# Requirements
Expand All @@ -38,7 +38,7 @@ Download ROSI source code from source (main)
1. Download zip of release source code (non-release source code are not recommended as they may contain issues not yet fixed for a release).
2. Unzip the folder folder, place it in a good location on your computer.
3. Install [NodeJS](https://nodejs.org/en/download) and [NPM](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) _(Required to build ROSI)_.
4. Run `npm i --save-dev` to download the required electron packages.
4. Run `npm install` to download the required build dependencies (including Electron).
5. View the package.json file to see the `npm run build` commands available.

# ROSI LTS Version
Expand Down
95 changes: 93 additions & 2 deletions THIRD‑PARTY‑NOTICES.md → THIRD-PARTY-NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ Jean-Philippe Aumasson (https://131002.net/siphash/siphash24.c)

The file Python/dtoa.c, which supplies C functions dtoa and strtod for conversion of C doubles to and from strings, is derived from the file of the same name by David M. Gay, currently available from https://web.archive.org/web/20220517033456/http://www.netlib.org/fp/dtoa.c. The original file, as retrieved on March 16, 2009, contains the following copyright and licensing notice:

/**************\*\***************\*\*\*\***************\*\***************
/******\*\*******\*\*******\*\*******\*\*\*\*******\*\*******\*\*******\*\*******

-
- The author of this software is David M. Gay.
Expand All @@ -575,7 +575,7 @@ The file Python/dtoa.c, which supplies C functions dtoa and strtod for conversio
- WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
- REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
- OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
- **************\*\***************\*\*\***************\*\***************/
- ******\*\*******\*\*******\*\*******\*\*\*******\*\*******\*\*******\*\*******/

---

Expand Down Expand Up @@ -1032,3 +1032,94 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

---

# Application framework and runtime

> This section is maintained by hand. The `licenses.json` produced by
> `npm-license-crawler --production` only covers ROSI's runtime npm
> dependencies and **cannot** capture Electron (a devDependency that is shipped
> at runtime), Chromium, Node.js, or the bundled native binaries. Those are
> enumerated here.

## Electron

License: MIT
Copyright (c) Electron contributors
Copyright (c) 2013-present GitHub Inc.
Link: https://github.com/electron/electron/blob/main/LICENSE

ROSI is built on Electron, which is redistributed in every ROSI build. Electron
bundles Chromium and Node.js (see below).

```
The MIT License (MIT)

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
```

## Chromium

License: BSD-3-Clause (plus a large set of bundled third-party components with
their own licenses).
Link: https://chromium.googlesource.com/chromium/src/+/refs/heads/main/LICENSE

The complete, version-specific Chromium credits/license set for the Electron
release ROSI ships is published with each Electron release as
`LICENSES.chromium.html`. See the Electron release that matches this build at
https://github.com/electron/electron/releases.

### Chromium bundled FFmpeg (libffmpeg) — LGPL-2.1

Electron's Chromium ships an LGPL-2.1 build of FFmpeg (`libffmpeg`), which is
**distinct** from the standalone GPL FFmpeg ROSI bundles for conversion (see the
FFmpeg entry above and `ffmpeg/SOURCE_OFFER.txt`). In compliance with LGPL-2.1,
the corresponding source for Electron's `libffmpeg` is available from the
matching Electron release (Electron publishes FFmpeg source/relink material with
its release artifacts) and from the upstream FFmpeg project at
https://git.ffmpeg.org/ffmpeg.git. License text:
https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html

## Node.js

License: MIT (Node.js core), plus incorporated components under their own
licenses (OpenSSL — Apache-2.0; ICU — Unicode license; libuv — MIT; zlib — zlib
license; and others).
Link: https://github.com/nodejs/node/blob/main/LICENSE

Node.js is bundled inside Electron and redistributed with ROSI. The full,
version-specific Node.js license (including all incorporated components) is in
the `LICENSE` file of the matching Node.js release at
https://github.com/nodejs/node/releases.

---

# Bundled yt-dlp binary

ROSI ships the official yt-dlp standalone binaries (`assets/yt-dlp*`). yt-dlp
itself is released into the public domain under The Unlicense, but the standalone
binary statically bundles additional components — including GPL/LGPL-licensed
code (e.g. mutagen, GPL-2.0) and a Python interpreter — whose notices are listed
in the yt-dlp / Python sections above.

A consolidated notice and a written source offer for the GPL/LGPL components
bundled inside the yt-dlp binary are shipped alongside the binary at
`assets/YT-DLP-NOTICES.txt`. The complete corresponding source is available from
the yt-dlp project (https://github.com/yt-dlp/yt-dlp) and the respective upstream
component projects.
51 changes: 51 additions & 0 deletions assets/YT-DLP-NOTICES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
yt-dlp Binary Distribution Notice
=================================

This application (ROSI) bundles and redistributes the official yt-dlp
standalone binary.

yt-dlp itself
-------------
License: The Unlicense (public domain dedication)
Project: https://github.com/yt-dlp/yt-dlp
License text: https://github.com/yt-dlp/yt-dlp/blob/master/LICENSE

Bundled components inside the standalone binary
-----------------------------------------------
The yt-dlp standalone binary is produced with PyInstaller and statically
bundles a Python interpreter plus several third-party components. Some of these
are licensed under the GPL/LGPL and therefore carry a source-availability
obligation:

Component License Source
--------------- ---------------------- --------------------------------------
yt-dlp The Unlicense https://github.com/yt-dlp/yt-dlp
mutagen GPL-2.0-or-later https://github.com/quodlibet/mutagen
AtomicParsley GPL-2.0-or-later https://github.com/wez/atomicparsley
pycryptodome Public domain + BSD-2 https://github.com/Legrandin/pycryptodome
websockets BSD-3-Clause https://github.com/python-websockets/websockets
certifi MPL-2.0 https://github.com/certifi/python-certifi
brotli/brotlicffi MIT https://github.com/google/brotli
Python PSF License v2 https://github.com/python/cpython

The authoritative, version-specific list of bundled components and their
licenses is published by the yt-dlp project at:
https://github.com/yt-dlp/yt-dlp/blob/master/THIRD_PARTY_LICENSES.txt
(When updating the bundled yt-dlp binary, drop the matching upstream
THIRD_PARTY_LICENSES.txt next to this file.)

Written Source Offer (GPLv2 Section 3(b))
-----------------------------------------
For the GPL-licensed components statically bundled in the yt-dlp binary
distributed with ROSI (e.g. mutagen, AtomicParsley), BurntToasters offers to
provide, for a charge no more than the cost of physically performing
distribution, the complete corresponding source code. This offer is valid for at
least three years from the date you received the binaries.

The corresponding source is also freely available from the upstream projects
listed above. To request source on physical media, contact:
BurntToasters <code@rosie.run>

ROSI invokes yt-dlp as an external process and is not a derivative work of
yt-dlp or any of its bundled components. ROSI is licensed under the Mozilla
Public License 2.0 (MPL-2.0).
11 changes: 11 additions & 0 deletions assets/ytdlp-checksums.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"_comment": "SHA-256 of the committed yt-dlp binaries. Regenerate ONLY after verifying the binaries against yt-dlp upstream SHA2-256SUMS. See build-scripts/check-ytdlp.js.",
"generatedAt": "2026-06-17T21:25:31.250Z",
"binaries": {
"yt-dlp.exe": "3a48cb955d55c8821b60ccbdbbc6f61bc958f2f3d3b7ad5eaf3d83a543293a27",
"yt-dlp_arm64.exe": "847583f91bb6d26479c1dc9643c2f4b8857a90b40d619da97b0cfabccb9138d0",
"yt-dlp_macos": "b82c3626952e6c14eaf654cc565866775ffd0b9ffb7021628ac59b42c2f4f244",
"yt-dlp_linux": "bf8aac79b72287a6d2043074415132558b43743a8f9461a22b0141e90f16ce66",
"yt-dlp_linux_aarch64": "cabd246445bdfde0eda0dfe68bbe90354be83f3fdbbf077df11a2ea55f41cdbd"
}
}
45 changes: 35 additions & 10 deletions build-scripts/check-coverage-thresholds.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,37 @@ if (!fs.existsSync(summaryPath)) {

const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8'));

// Per-file floors for the security-critical / non-trivial main-process and
// shared modules. Each is set a few points below the currently-measured
// coverage so the gate catches regressions without being flaky. All four
// metrics (lines/statements/branches/functions) are enforced.
//
// NOTE: renderer modules (rosiEngine.ts, modules/*.ts) are intentionally absent.
// They are exercised via on-the-fly transpile+eval in the test suite, which v8
// coverage cannot attribute to the source files, so they report 0% here. Add
// floors for them only once the renderer tests import the compiled artifact.
const thresholds = {
'src/main/main.ts': { lines: 12, statements: 12 },
'src/main/preload.ts': { lines: 80, statements: 80 },
'src/main/processKill.ts': { lines: 80, statements: 80 },
'src/utils/validation.ts': { lines: 85, statements: 85 },
'src/utils/downloadLifecycle.ts': { lines: 90, statements: 90 },
'src/main/main.ts': { lines: 80, statements: 80, branches: 70, functions: 80 },
'src/main/downloader.ts': { lines: 88, statements: 88, branches: 72, functions: 88 },
'src/main/platform.ts': { lines: 68, statements: 68, branches: 58, functions: 58 },
'src/main/settings.ts': { lines: 88, statements: 88, branches: 82, functions: 90 },
'src/main/updater.ts': { lines: 92, statements: 92, branches: 85, functions: 92 },
'src/main/download/commandBuilders.ts': {
lines: 88,
statements: 88,
branches: 86,
functions: 90,
},
'src/main/download/videoInfo.ts': { lines: 70, statements: 70, branches: 60, functions: 70 },
'src/main/preload.ts': { lines: 90, statements: 90, branches: 90, functions: 90 },
'src/main/processKill.ts': { lines: 90, statements: 90, branches: 85, functions: 55 },
'src/utils/ipcValidation.ts': { lines: 85, statements: 85, branches: 85, functions: 88 },
'src/utils/validation.ts': { lines: 85, statements: 82, branches: 72, functions: 90 },
'src/utils/downloadLifecycle.ts': { lines: 95, statements: 95, branches: 95, functions: 95 },
};

const METRICS = ['lines', 'statements', 'branches', 'functions'];

function findCoverageEntry(suffix) {
const normalizedSuffix = suffix.replace(/\\/g, '/');
return Object.entries(summary).find(([key]) =>
Expand All @@ -35,11 +58,13 @@ for (const [file, threshold] of Object.entries(thresholds)) {
}

const [, metrics] = match;
if (metrics.lines.pct < threshold.lines) {
failures.push(`${file}: lines ${metrics.lines.pct}% < ${threshold.lines}%`);
}
if (metrics.statements.pct < threshold.statements) {
failures.push(`${file}: statements ${metrics.statements.pct}% < ${threshold.statements}%`);
for (const metric of METRICS) {
if (typeof threshold[metric] !== 'number') continue;
const actual =
metrics[metric] && typeof metrics[metric].pct === 'number' ? metrics[metric].pct : 0;
if (actual < threshold[metric]) {
failures.push(`${file}: ${metric} ${actual}% < ${threshold[metric]}%`);
}
}
}

Expand Down
Loading
Loading