Свой родной GUI для yt-dlp на Electron и React
- понедельник, 18 августа 2025 г. в 00:00:03
Честно, я устал каждый месяц искать новый способ для скачивания различного контента с youtube. Что сегодня работает — завтра уже не подает признаков жизни. После очередной смерти одного из загрузчиков — я понял, что проще уже сделать свой, чем вот так тратить время на постоянные поиски.
Тут все просто. Просмотрев некоторые варианты, я понял, что выбирать не приходиться. Интерфейсы есть, но они довольно ограничены в функционале и больше подходят для тех, кто хочет просто скачать пару видео в дорогу. Мне же нужен был полноценный инструмент, который закроет все мои нужды, как монтажера.
Что именно я хотел получить:
Загрузка видео в разных форматах + форматирование
Возможность скачать аудио отдельно
Скачивание отдельной части видео или аудио
Работа со всеми видами ссылок (в том числе плейлисты, джемы, live и shorts)
Отслеживание кодека и битрейта перед скачиванием
Ну и конечно, чтобы это визуально не выглядело - как взлом терминала из fallout.
Давно хотел попробовать что-нибудь кроме python, и раз уж тут подвернулась такая возможность - то почему мы бы и нет.
Хватит лишней болтовни - переходим к главному.
И да, интерфейс пока работает только на WINDOWS.
Встречают по одежке, как говорится.
Поначалу не хотел тратить много времени и остановиться на чем-нибудь однотонном. Но в процессе увлекся и меня немного занесло. Сделал две темы (светлая и тёмная).
В центр разместил забавную гифку, чтобы было не так грустно. Так же дополнительно сделал меню настроек. Туда вывел выбор папки для скачивания, выбор языка, темы, настройку -geopass (выключена по умолчанию) и уведомления.
Сам коддля «настроек» вывел в отдельные файлы (settings.js и settings.html) чтобы не путаться.
Далее идет основной интерфейс для выбора настроек скачивания. Я не буду показывать каждую кнопку, чтобы не растягивать статью.
Приложение построил на Electron для десктопа и React для фронтенда. Electron обрабатывает IPC, процессы и внешние вызовы, React — UI с состояниями.
Функционал
Проверка URL видео
Функция проверки URL реализована в main.js
через IPC-событие check-url
. Она использует yt-dlp для получения метаданных видео.
ipcMain.handle('check-url', async (event, url) => {
try {
const ytDlpPath = getYtDlpPath();
await fs.access(ytDlpPath); // Проверка наличия yt-dlp.exe
const args = ['--dump-single-json', url];
const process = spawn(ytDlpPath, args, { shell: true });
let output = '';
process.stdout.on('data', (data) => {
output += data.toString();
});
return new Promise((resolve, reject) => {
process.on('close', (code) => {
if (code === 0) {
try {
const json = JSON.parse(output);
resolve(json); // Возвращает метаданные: миниатюра, форматы, длительность
} catch (err) {
reject(new Error('Ошибка парсинга JSON'));
}
} else {
reject(new Error(`yt-dlp завершился с кодом ${code}`));
}
});
});
} catch (err) {
console.error('Ошибка проверки URL:', err);
throw err;
}
});
Вызывается yt-dlp с параметром --dump-single-json
, который возвращает JSON с информацией о видео (название, форматы, длительность). В App.jsx результат отображается в интерфейсе (миниатюра, список форматов).
Скачивание
Скачивание реализовано в main.js
через IPC start-download
. Формируются аргументы для yt-dlp и запускается процесс.
ipcMain.handle('start-download', async (event, options) => {
const { url, format, quality, startTime, endTime, downloadMode } = options;
const ytDlpPath = getYtDlpPath();
const ffmpegPath = getFfmpegPath();
let args = [url, '-o', '%(title)s.%(ext)s'];
if (format === 'mp3' || format === 'm4a') {
args.push('-x', `--audio-format=${format}`, `--audio-quality=${quality || 0}`);
} else {
args.push(`-f bestvideo[height<=${quality}]+bestaudio/best[height<=${quality}]`);
}
if (startTime && endTime) {
args.push(`--download-sections=*${startTime}-${endTime}`);
}
const process = spawn(ytDlpPath, args, { shell: true, env: { ...process.env, PATH: `${process.env.PATH};${path.dirname(ffmpegPath)}` } });
currentDownloadProcess = process;
process.stdout.on('data', (data) => {
const output = data.toString();
const match = output.match(/(\d+\.\d)%/); // Парсинг прогресса
if (match) {
mainWindow.webContents.send('download-progress', { percent: parseFloat(match[1]) });
}
});
return new Promise((resolve, reject) => {
process.on('close', (code) => {
if (code === 0) {
mainWindow.webContents.send('download-finished');
resolve();
} else if (!isCancelled) {
mainWindow.webContents.send('download-error', { message: `Ошибка скачивания, код ${code}` });
reject(new Error(`Ошибка скачивания, код ${code}`));
}
});
});
});
Формируются аргументы для yt-dlp в зависимости от формата и качества. Прогресс парсится из stdout через regex. События download-progress
и download-finished
отправляются в React для обновления UI.
И на сладкое — скачивание аудио или видео частями.
Буду честен — я до конца не верил, что смогу такое реализовать. Штука крайне удобная, но не без минусов. Она хорошо себя показывает в длинных роликах (от 30–40 минут), но в коротких проще скачать видео полностью и отрезать лишнее (короткий ролик загрузиться быстрее, чем его часть. Окак :-)
Интерфейс (фронтенд):
Пользователь включает опцию "Скачать часть" кнопкой, активируя слайдер (react-range
).
Слайдер задаёт startTime
и endTime
(в секундах) в пределах длительности видео (videoInfo.duration
).
Временные метки отображаются в формате HH:MM:SS
.
const [rangeValues, setRangeValues] = useState([0, 0]);
const [showRange, setShowRange] = useState(false);
<Range
values={rangeValues}
step={1}
min={0}
max={videoInfo?.duration || 100}
onChange={(values) => setRangeValues(values)}
/>
При нажатии "Скачать" данные передаются в бэкенд через IPC:
const handleDownload = async () => {
const options = {
url, format, quality,
startTime: showRange ? Math.floor(rangeValues[0]) : null,
endTime: showRange ? Math.floor(rangeValues[1]) : null
};
await window.electronAPI.startDownload(options);
};
Бэкенд (main.js):
Получает startTime
и endTime
через IPC start-download
.
Добавляет аргумент --download-sections=*${startTime}-${endTime}
для yt-dlp.
Запускает yt-dlp с ffmpeg для обрезки.
Зачем ffmpeg?
ffmpeg (бинарник в bin/) используется yt-dlp для обрезки медиа и конвертации (например, видео в MP3). Он обеспечивает точную обрезку, но границы могут слегка варьироваться из-за ключевых кадров
Чтобы получать корректные данные о видео приходится цеплять cookies файл через встроенный electron браузер (вход в аккаунт google). Я пытался обойти это сменой способа извлечения данных о видео с tv_embedded
на mweb
. Два дня адских мучений спустя (связанных с интеграцией плагина для извлечения PO Token сначала на VPS, а потом и в само приложение) я решил отказаться от этой затеи.
Это были веселые пару недель в мои жизни, будем честны. Все функции, которые хотел увидеть - я смог реализовать. Да, есть некоторые некритичные баги, которые нужно в скором времени починить. Если кому-то будет интересно протестировать GUI - буду только рад.
Из того, что хотел бы ещё добавить в будущем - это поддержка linux/mac и возможность полноценной работы с плейлистами (пока можно скачивать только конкретное видео из плейлиста)
Проект планируется платным, но для желающих потыкаться - PROMO_0Y1NE52T
P.S - Для использования приложения на территории РФ необходимо включать методы для обхода замедления (галочка в настройках не спасает)
Полезные ссылки: