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
2 changes: 1 addition & 1 deletion .github/workflows/test-all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ permissions:
contents: read

env:
NODE_VERSION: '22'
NODE_VERSION: '24'

jobs:
lint:
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
22
24
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ ROSI is an Electron GUI for yt-dlp

[<img src="https://get.microsoft.com/images/en-us%20dark.svg" width="200"/>](https://apps.microsoft.com/detail/9p4q134b2jw3?referrer=appbadge&mode=direct) [<img width="150" alt="ROSI" src="https://prod.rosie.run/img/download-for-windows.png"/>](https://github.com/BurntToasters/ROSI/releases/latest/download/ROSI-Windows-x64.exe) [<img width="150" alt="ROSI" src="https://prod.rosie.run/img/download-for-windows-arm64.png"/>](https://github.com/BurntToasters/ROSI/releases/latest/download/ROSI-Windows-arm64.exe) [<img width="150" alt="ROSI" src="https://prod.rosie.run/img/download-for-macos.png"/>](https://github.com/BurntToasters/ROSI/releases/latest/download/ROSI-MacOS-universal.dmg) [<img width="150" alt="ROSI" src="https://prod.rosie.run/img/download-for-linux.png"/>](https://github.com/BurntToasters/ROSI/releases/latest)

<p align="center"><img width="700" src="https://prod.rosie.run/img/rosi/ROSI.png"></p>
<p align="center">

<img width="45%" height="1012" alt="ROSI-3-1-1" src="https://github.com/user-attachments/assets/e66cebfb-6925-409d-9843-e94cfd868891" />
&nbsp;
<img width="45%" height="1012" alt="ROSI-3-x-app" src="https://github.com/user-attachments/assets/7a15cea3-1f32-432e-a142-aaded90c9f2a" />

</p>

# LICENSES

Expand Down
90 changes: 90 additions & 0 deletions build-scripts/build-with-restore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
const { spawnSync } = require('child_process');
const path = require('path');

const args = process.argv.slice(2);

function usage() {
console.error(
'Usage: node build-scripts/build-with-restore.js [--prepare <script>] [--env KEY=VALUE] -- <command> [args...]'
);
process.exit(1);
}

function resolveCommand(command) {
if (process.platform === 'win32' && (command === 'npm' || command === 'npx')) {
return `${command}.cmd`;
}
return command;
}

function run(command, commandArgs, envOverrides) {
const result = spawnSync(resolveCommand(command), commandArgs, {
stdio: 'inherit',
env: { ...process.env, ...envOverrides },
shell: false,
});

if (result.error) {
console.error(result.error.message);
return 1;
}
return result.status ?? 1;
}

let prepareScript = null;
const envOverrides = {};
const separatorIndex = args.indexOf('--');

if (separatorIndex === -1) {
usage();
}

const optionArgs = args.slice(0, separatorIndex);
const commandParts = args.slice(separatorIndex + 1);
if (commandParts.length === 0) {
usage();
}

for (let i = 0; i < optionArgs.length; i++) {
const current = optionArgs[i];
if (current === '--prepare') {
const next = optionArgs[i + 1];
if (!next) usage();
prepareScript = next;
i++;
continue;
}

if (current === '--env') {
const next = optionArgs[i + 1];
if (!next || !next.includes('=')) usage();
const [key, ...valueParts] = next.split('=');
envOverrides[key] = valueParts.join('=');
i++;
continue;
}

usage();
}

let exitCode = 0;

try {
if (prepareScript) {
exitCode = run(process.execPath, [path.resolve(prepareScript)], envOverrides);
}

if (exitCode === 0) {
const [command, ...commandArgs] = commandParts;
exitCode = run(command, commandArgs, envOverrides);
}
} finally {
if (prepareScript) {
const restoreCode = run(process.execPath, [path.join(__dirname, 'restore-binaries.js')], {});
if (exitCode === 0 && restoreCode !== 0) {
exitCode = restoreCode;
}
}
}

process.exit(exitCode);
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 12 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rosi",
"version": "3.4.2",
"version": "3.5.0",
"private": true,
"description": "Electron GUI for yt-dlp",
"keywords": [
Expand All @@ -12,7 +12,7 @@
"main": "dist/main/main.js",
"packageManager": "npm@11.10.1",
"engines": {
"node": ">=22.x",
"node": ">=24.x",
"npm": ">=10.x"
},
"scripts": {
Expand Down Expand Up @@ -44,18 +44,18 @@
"build:win:all": "npm run build:win && npm run build:win:x64 && npm run build:win:arm64",
"build:mac:universal": "npm run prebuild && dotenv -e .env -- electron-builder -c electron-builder.base.yml --mac --universal",
"build:linux": "npm run prebuild && electron-builder -c electron-builder.base.yml --linux AppImage deb rpm --x64 --arm64",
"build:linux:x64": "node build-scripts/prepare-linux-x64.js && electron-builder -c electron-builder.base.yml --linux AppImage deb rpm --x64 && node build-scripts/restore-binaries.js",
"build:linux:arm64": "node build-scripts/prepare-linux-arm64.js && electron-builder -c electron-builder.base.yml --linux AppImage deb rpm --arm64 && node build-scripts/restore-binaries.js",
"build:linux:x64": "node build-scripts/build-with-restore.js --prepare build-scripts/prepare-linux-x64.js -- npx electron-builder -c electron-builder.base.yml --linux AppImage deb rpm --x64",
"build:linux:arm64": "node build-scripts/build-with-restore.js --prepare build-scripts/prepare-linux-arm64.js -- npx electron-builder -c electron-builder.base.yml --linux AppImage deb rpm --arm64",
"build:linux:all": "npm run prebuild && npm run build:linux:x64 && npm run build:linux:arm64",
"build:linux:native": "npm run prebuild && npm run build:linux:native:x64 && npm run build:linux:native:arm64",
"build:linux:native:x64": "node build-scripts/prepare-linux-x64.js && cross-env USE_SYSTEM_FPM=true electron-builder -c electron-builder.base.yml --linux AppImage deb rpm --x64 && node build-scripts/restore-binaries.js",
"build:linux:native:arm64": "node build-scripts/prepare-linux-arm64.js && cross-env USE_SYSTEM_FPM=true electron-builder -c electron-builder.base.yml --linux AppImage deb rpm --arm64 && node build-scripts/restore-binaries.js",
"build:linux:native:x64": "node build-scripts/build-with-restore.js --prepare build-scripts/prepare-linux-x64.js --env USE_SYSTEM_FPM=true -- npx electron-builder -c electron-builder.base.yml --linux AppImage deb rpm --x64",
"build:linux:native:arm64": "node build-scripts/build-with-restore.js --prepare build-scripts/prepare-linux-arm64.js --env USE_SYSTEM_FPM=true -- npx electron-builder -c electron-builder.base.yml --linux AppImage deb rpm --arm64",
"build:linux:native:all": "npm run prebuild && npm run build:linux:native:x64 && npm run build:linux:native:arm64",
"build:github": "npm run prebuild && dotenv -e .env -- cross-env CHANNEL=github electron-builder -c electron-builder.github.yml --win --arm64 --x64",
"build:github:win": "npm run prebuild && dotenv -e .env -- cross-env CHANNEL=github electron-builder -c electron-builder.github.yml --win --arm64 --x64",
"build:github:mac": "npm run prebuild && dotenv -e .env -- cross-env CHANNEL=github electron-builder -c electron-builder.github.yml --mac --universal",
"build:msstore:x64": "cross-env CHANNEL=msstore node build-scripts/prepare-x64.js && electron-builder --win --x64 -c electron-builder.msstore.yml && node build-scripts/restore-binaries.js",
"build:msstore:arm64": "cross-env CHANNEL=msstore node build-scripts/prepare-arm64.js && electron-builder --win --arm64 -c electron-builder.msstore.yml && node build-scripts/restore-binaries.js",
"build:msstore:x64": "node build-scripts/build-with-restore.js --prepare build-scripts/prepare-x64.js --env CHANNEL=msstore -- npx electron-builder --win --x64 -c electron-builder.msstore.yml",
"build:msstore:arm64": "node build-scripts/build-with-restore.js --prepare build-scripts/prepare-arm64.js --env CHANNEL=msstore -- npx electron-builder --win --arm64 -c electron-builder.msstore.yml",
"build:msstore": "npm run prebuild && npm run build:msstore:x64 && npm run build:msstore:arm64",
"restore-binaries": "node build-scripts/restore-binaries.js",
"sign:gpg": "dotenv -e .env -- node build-scripts/gpg-sign.js",
Expand All @@ -68,10 +68,10 @@
"release:linux:chain": "npm run prebuild && npm run release:linux:x64 && npm run release:linux:arm64",
"release:linux:chain:f": "npm run prebuild && npm run release:linux:native:x64 && npm run release:linux:native:arm64",
"release:linux": "npm run prebuild && dotenv -e .env -- cross-env CHANNEL=github electron-builder -c electron-builder.github.yml --linux AppImage deb rpm --x64 --arm64 --publish always && npm run sign:gpg",
"release:linux:x64": "node build-scripts/prepare-linux-x64.js && dotenv -e .env -- cross-env CHANNEL=github electron-builder -c electron-builder.github.yml --linux AppImage deb rpm --x64 --publish always && node build-scripts/restore-binaries.js && npm run sign:gpg:x64",
"release:linux:arm64": "node build-scripts/prepare-linux-arm64.js && dotenv -e .env -- cross-env CHANNEL=github electron-builder -c electron-builder.github.yml --linux AppImage deb rpm --arm64 --publish always && node build-scripts/restore-binaries.js && npm run sign:gpg:arm64",
"release:linux:native:x64": "node build-scripts/prepare-linux-x64.js && dotenv -e .env -- cross-env USE_SYSTEM_FPM=true CHANNEL=github electron-builder -c electron-builder.github.yml --linux AppImage deb rpm --x64 --publish always && node build-scripts/restore-binaries.js && npm run sign:gpg:x64",
"release:linux:native:arm64": "node build-scripts/prepare-linux-arm64.js && dotenv -e .env -- cross-env USE_SYSTEM_FPM=true CHANNEL=github electron-builder -c electron-builder.github.yml --linux AppImage deb rpm --arm64 --publish always && node build-scripts/restore-binaries.js && npm run sign:gpg:arm64",
"release:linux:x64": "dotenv -e .env -- node build-scripts/build-with-restore.js --prepare build-scripts/prepare-linux-x64.js --env CHANNEL=github -- npx electron-builder -c electron-builder.github.yml --linux AppImage deb rpm --x64 --publish always && npm run sign:gpg:x64",
"release:linux:arm64": "dotenv -e .env -- node build-scripts/build-with-restore.js --prepare build-scripts/prepare-linux-arm64.js --env CHANNEL=github -- npx electron-builder -c electron-builder.github.yml --linux AppImage deb rpm --arm64 --publish always && npm run sign:gpg:arm64",
"release:linux:native:x64": "dotenv -e .env -- node build-scripts/build-with-restore.js --prepare build-scripts/prepare-linux-x64.js --env USE_SYSTEM_FPM=true --env CHANNEL=github -- npx electron-builder -c electron-builder.github.yml --linux AppImage deb rpm --x64 --publish always && npm run sign:gpg:x64",
"release:linux:native:arm64": "dotenv -e .env -- node build-scripts/build-with-restore.js --prepare build-scripts/prepare-linux-arm64.js --env USE_SYSTEM_FPM=true --env CHANNEL=github -- npx electron-builder -c electron-builder.github.yml --linux AppImage deb rpm --arm64 --publish always && npm run sign:gpg:arm64",
"release:all": "npm run prebuild && npm run release:mac && npm run release:win && npm run release:linux"
},
"overrides": {
Expand Down
8 changes: 6 additions & 2 deletions src/main/deno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as fs from 'fs';
import { spawn } from 'child_process';
import { BrowserWindow, dialog } from 'electron';
import log from 'electron-log/main';
import type { MessageBoxOptions } from 'electron';
import { DENO_CHECK_TIMEOUT_MS, DENO_INSTALL_TIMEOUT_MS } from './constants';
import { isWindows } from './platform';

Expand Down Expand Up @@ -91,14 +92,17 @@ export async function installDeno(
mainWindow: BrowserWindow | null
): Promise<{ success?: boolean; cancelled?: boolean; output?: string; error?: string }> {
const parentWindow = BrowserWindow.getFocusedWindow() || mainWindow;
const confirm = await dialog.showMessageBox(parentWindow!, {
const confirmOptions: MessageBoxOptions = {
type: 'warning',
buttons: ['Install', 'Cancel'],
defaultId: 0,
cancelId: 1,
message:
'This will download and run the Deno installer from deno.land. Do you want to continue?',
});
};
const confirm = parentWindow
? await dialog.showMessageBox(parentWindow, confirmOptions)
: await dialog.showMessageBox(confirmOptions);

if (confirm.response !== 0) {
return { cancelled: true };
Expand Down
108 changes: 108 additions & 0 deletions src/main/download/commandBuilders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import type { DownloadRequestOptions, Settings } from '../../types';

export function resolveVideoEncoder(settings: Settings): string {
if (!settings.gpuAcceleration) return 'copy';
if (settings.gpuType === 'nvidia') return 'h264_nvenc';
if (settings.gpuType === 'amd') return 'h264_amf';
if (settings.gpuType === 'intel') return 'h264_qsv';
return 'copy';
}

export function buildFfmpegArgs(
inputPath: string,
outputPath: string,
targetFormat: string,
videoEncoder: string
): string[] {
if (targetFormat === 'mp3' || targetFormat === 'm4a') {
return [
'-i',
inputPath,
'-vn',
'-c:a',
targetFormat === 'mp3' ? 'libmp3lame' : 'aac',
'-y',
outputPath,
];
}

return [
'-i',
inputPath,
'-c:v',
videoEncoder,
'-c:a',
'aac',
'-movflags',
'+faststart',
'-y',
outputPath,
];
}

interface BuildYtdlpArgsInput {
normalizedDownloadDir: string;
url: string;
settings: Settings;
options: DownloadRequestOptions;
ffmpegLocation: string | null;
}

export interface BuildYtdlpArgsResult {
args: string[];
statusMessages: string[];
}

export function buildYtdlpArgs({
normalizedDownloadDir,
url,
settings,
options,
ffmpegLocation,
}: BuildYtdlpArgsInput): BuildYtdlpArgsResult {
const args = [
'-P',
normalizedDownloadDir,
'--no-playlist',
'--print',
'after_move:filepath',
'--newline',
'--progress',
'--progress-delta',
'1',
'-f',
settings.bestQuality ? 'bestvideo+bestaudio/best' : 'best[ext=mp4]/best[ext=webm]/best',
url,
];
const statusMessages: string[] = [];

if (ffmpegLocation) {
args.splice(args.length - 1, 0, '--ffmpeg-location', ffmpegLocation);
}

const formatFlagIndex = args.indexOf('-f');
if (options.videoFormat && options.audioFormat) {
args[formatFlagIndex + 1] = `${options.videoFormat}+${options.audioFormat}`;
statusMessages.push(
`📹 Using formats: video=${options.videoFormat}, audio=${options.audioFormat}`
);
} else if (options.videoFormat) {
args[formatFlagIndex + 1] = options.videoFormat;
statusMessages.push(`📹 Using video format: ${options.videoFormat}`);
} else if (options.audioFormat) {
args[formatFlagIndex + 1] = options.audioFormat;
statusMessages.push(`🎵 Using audio format: ${options.audioFormat}`);
}

if (settings.audioOnly && !options.videoFormat && !options.audioFormat) {
args.splice(formatFlagIndex, 2);
args.splice(-1, 0, '-x', '--audio-format', 'mp3', '--audio-quality', '0');
statusMessages.push('🎵 Audio-only mode enabled');
}

if (settings.hookBrowser && settings.browserChoice) {
args.splice(-1, 0, '--cookies-from-browser', settings.browserChoice);
}

return { args, statusMessages };
}
Loading