Релиз v1.0.0

This commit is contained in:
2026-08-09 11:43:39 +03:00
commit 0c214ef9bd
8 changed files with 734 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
# Результаты работы
output/*
!output/.gitkeep
# Локальные зависимости
tools/*
!tools/.gitkeep
# Временные файлы
temp/
*.tmp
*.part
*.ytdl
# PowerShell / Windows
*.log
Thumbs.db
Desktop.ini
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026
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.
+207
View File
@@ -0,0 +1,207 @@
# YouTube Tools
Набор PowerShell-скриптов для Windows для скачивания видео и аудио с YouTube, а также создания видео с закадровым переводом.
> Используйте утилиты только для контента, который вы имеете право скачивать и обрабатывать.
## Возможности
В репозитории находятся три скрипта:
| Скрипт | Назначение |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `Get-YouTubeAudio.ps1` | Скачивание лучшей аудиодорожки и конвертация в FLAC |
| `Get-YouTubeVideo.ps1` | Скачивание видео в максимально доступном качестве |
| `Get-YouTubeVideoTranslate.ps1` | Скачивание видео, получение закадрового перевода через `vot-cli` и смешивание перевода с оригинальным звуком |
Все готовые файлы сохраняются в папку:
```text
output/
```
## Структура проекта
```text
youtube-tools/
├── scripts/
│ ├── Get-YouTubeAudio.ps1
│ ├── Get-YouTubeVideo.ps1
│ └── Get-YouTubeVideoTranslate.ps1
│
├── tools/
│ ├── yt-dlp.exe
│ ├── ffmpeg.exe
│ └── ...
│
├── output/
│ └── .gitkeep
│
├── README.md
├── LICENSE
└── .gitignore
```
Бинарные зависимости в Git не добавляются. Папка `tools` предназначена для локального размещения необходимых программ.
## Поиск зависимостей
Каждый скрипт ищет необходимые программы в следующем порядке:
1. В локальной папке `tools`.
2. В системной переменной `PATH`.
3. Если программа не найдена нигде, скрипт показывает ошибку и завершает работу.
Таким образом, можно использовать как локальные версии утилит, так и программы, установленные в Windows.
## Зависимости
Скачать всё с облака:
[Скачать (.zip)] (https://cloud.kolbox.duckdns.org/lib/b5d0b4e0-fd0c-40fe-b799-3ab0015550d0/file/Gitea%20%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B8/youtube-tools/tools.zip?dl=1)
### yt-dlp
Используется для скачивания видео и аудио с YouTube.
Последняя версия:
https://github.com/yt-dlp/yt-dlp/releases/latest
Для локального использования положите `yt-dlp.exe` в:
```text
tools/yt-dlp.exe
```
### FFmpeg
Используется для конвертации аудио, объединения потоков и обработки звуковых дорожек.
Сборка FFmpeg для Windows:
https://www.gyan.dev/ffmpeg/builds/
После распаковки архива возьмите из папки `bin` файл:
```text
ffmpeg.exe
```
и положите его в:
```text
tools/ffmpeg.exe
```
`ffprobe.exe` текущим скриптам напрямую не требуется.
### vot-cli
Используется только скриптом `Get-YouTubeVideoTranslate.ps1`.
Репозиторий:
https://github.com/FOSWLY/vot-cli
Установка:
```powershell
npm install -g vot-cli
```
После установки должна быть доступна команда:
```powershell
vot-cli
```
Скрипт также умеет искать локальный `vot-cli.cmd`, `vot-cli.exe` или `vot-cli.ps1` в папке `tools`.
### Node.js и npm
Необходимы для установки и работы `vot-cli`.
Проверка:
```powershell
node --version
npm --version
```
Скрипт перевода проверяет `node` и `npm` как в папке `tools`, так и в системном `PATH`.
## Зависимости по скриптам
| Скрипт | yt-dlp | FFmpeg | Node.js / npm | vot-cli |
| ------------------------------- |:------:|:------:|:-------------:|:-------:|
| `Get-YouTubeAudio.ps1` | ✓ | ✓ | — | — |
| `Get-YouTubeVideo.ps1` | ✓ | ✓ | — | — |
| `Get-YouTubeVideoTranslate.ps1` | ✓ | ✓ | ✓ | ✓ |
## Использование
### Скачивание аудио
Запустите:
```powershell
.\scripts\Get-YouTubeAudio.ps1
```
Введите ссылку на YouTube. Результат будет сохранён в `output` в формате FLAC.
### Скачивание видео
Запустите:
```powershell
.\scripts\Get-YouTubeVideo.ps1
```
Введите ссылку на YouTube. Скрипт скачает видео в максимально доступном качестве и сохранит результат в `output`.
### Видео с закадровым переводом
Запустите:
```powershell
.\scripts\Get-YouTubeVideoTranslate.ps1
```
Можно ввести одну ссылку или несколько ссылок через запятую.
Скрипт:
1. скачивает исходное видео;
2. получает переведённую дорожку через `vot-cli`;
3. уменьшает громкость оригинального звука до `20%`;
4. смешивает оригинальный звук с переводом;
5. сохраняет итоговый MP4 в `output`;
6. удаляет временные файлы.
## Политика выполнения PowerShell
Если Windows запрещает запуск `.ps1`, можно разрешить выполнение локальных скриптов для текущего пользователя:
```powershell
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
```
Изменяйте политику выполнения только если понимаете последствия этой настройки.
## Выходные файлы
Все результаты находятся в:
```text
youtube-tools/output/
```
Папка создаётся автоматически, если отсутствует.
Содержимое `output` исключено из Git.
## Лицензия
MIT. См. файл `LICENSE`.
View File
+123
View File
@@ -0,0 +1,123 @@
#requires -Version 5.1
$ErrorActionPreference = "Stop"
# Корень проекта определяется независимо от текущей папки PowerShell.
$ProjectRoot = Split-Path -Parent $PSScriptRoot
$ToolsDir = Join-Path $ProjectRoot "tools"
$OutputDir = Join-Path $ProjectRoot "output"
if (-not (Test-Path $OutputDir)) {
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
}
function Find-Tool {
param(
[Parameter(Mandatory = $true)]
[string]$Name,
[string[]]$LocalNames = @()
)
foreach ($localName in $LocalNames) {
$localPath = Join-Path $ToolsDir $localName
if (Test-Path $localPath -PathType Leaf) {
return (Resolve-Path $localPath).Path
}
}
$command = Get-Command $Name -ErrorAction SilentlyContinue
if ($command) {
return $command.Source
}
return $null
}
function Require-Tool {
param(
[Parameter(Mandatory = $true)]
[string]$Name,
[string[]]$LocalNames = @(),
[string]$InstallHint = ""
)
$tool = Find-Tool -Name $Name -LocalNames $LocalNames
if (-not $tool) {
Write-Host ""
Write-Host "ОШИБКА: не найдена зависимость '$Name'." -ForegroundColor Red
Write-Host "Проверены:"
Write-Host " 1. Папка: $ToolsDir"
Write-Host " 2. Системная переменная PATH"
if ($InstallHint) {
Write-Host ""
Write-Host $InstallHint
}
Write-Host ""
Read-Host "Нажмите Enter для выхода"
exit 1
}
return $tool
}
$YtDlp = Require-Tool `
-Name "yt-dlp" `
-LocalNames @("yt-dlp.exe", "yt-dlp.cmd", "yt-dlp.ps1") `
-InstallHint "Скачать yt-dlp: https://github.com/yt-dlp/yt-dlp/releases/latest"
$Ffmpeg = Require-Tool `
-Name "ffmpeg" `
-LocalNames @("ffmpeg.exe") `
-InstallHint "Скачать FFmpeg: https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full.7z"
$FfmpegDir = Split-Path -Parent $Ffmpeg
Write-Host ""
Write-Host "============================================================"
Write-Host " СКАЧИВАНИЕ АУДИО С YOUTUBE"
Write-Host "============================================================"
Write-Host "Результат: $OutputDir"
Write-Host ""
$Url = Read-Host "Введите URL видео с YouTube"
if ([string]::IsNullOrWhiteSpace($Url)) {
Write-Host ""
Write-Host "ОШИБКА: ссылка не введена." -ForegroundColor Red
Read-Host "Нажмите Enter для выхода"
exit 1
}
$OutputTemplate = Join-Path $OutputDir "%(title)s.%(ext)s"
Write-Host ""
Write-Host "Скачивание и конвертация аудио в FLAC..."
Write-Host ""
& $YtDlp `
"-f" "ba" `
"--extract-audio" `
"--audio-format" "flac" `
"--audio-quality" "0" `
"--ffmpeg-location" $FfmpegDir `
"-o" $OutputTemplate `
$Url
if ($LASTEXITCODE -ne 0) {
Write-Host ""
Write-Host "ОШИБКА: yt-dlp завершил работу с кодом $LASTEXITCODE." -ForegroundColor Red
Read-Host "Нажмите Enter для выхода"
exit $LASTEXITCODE
}
Write-Host ""
Write-Host "Готово." -ForegroundColor Green
Write-Host "Файл сохранён в: $OutputDir"
Write-Host ""
Read-Host "Нажмите Enter для выхода"
+120
View File
@@ -0,0 +1,120 @@
#requires -Version 5.1
$ErrorActionPreference = "Stop"
# Корень проекта определяется независимо от текущей папки PowerShell.
$ProjectRoot = Split-Path -Parent $PSScriptRoot
$ToolsDir = Join-Path $ProjectRoot "tools"
$OutputDir = Join-Path $ProjectRoot "output"
if (-not (Test-Path $OutputDir)) {
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
}
function Find-Tool {
param(
[Parameter(Mandatory = $true)]
[string]$Name,
[string[]]$LocalNames = @()
)
foreach ($localName in $LocalNames) {
$localPath = Join-Path $ToolsDir $localName
if (Test-Path $localPath -PathType Leaf) {
return (Resolve-Path $localPath).Path
}
}
$command = Get-Command $Name -ErrorAction SilentlyContinue
if ($command) {
return $command.Source
}
return $null
}
function Require-Tool {
param(
[Parameter(Mandatory = $true)]
[string]$Name,
[string[]]$LocalNames = @(),
[string]$InstallHint = ""
)
$tool = Find-Tool -Name $Name -LocalNames $LocalNames
if (-not $tool) {
Write-Host ""
Write-Host "ОШИБКА: не найдена зависимость '$Name'." -ForegroundColor Red
Write-Host "Проверены:"
Write-Host " 1. Папка: $ToolsDir"
Write-Host " 2. Системная переменная PATH"
if ($InstallHint) {
Write-Host ""
Write-Host $InstallHint
}
Write-Host ""
Read-Host "Нажмите Enter для выхода"
exit 1
}
return $tool
}
$YtDlp = Require-Tool `
-Name "yt-dlp" `
-LocalNames @("yt-dlp.exe", "yt-dlp.cmd", "yt-dlp.ps1") `
-InstallHint "Скачать yt-dlp: https://github.com/yt-dlp/yt-dlp/releases/latest"
$Ffmpeg = Require-Tool `
-Name "ffmpeg" `
-LocalNames @("ffmpeg.exe") `
-InstallHint "Скачать FFmpeg: https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full.7z"
$FfmpegDir = Split-Path -Parent $Ffmpeg
Write-Host ""
Write-Host "============================================================"
Write-Host " СКАЧИВАНИЕ ВИДЕО С YOUTUBE"
Write-Host "============================================================"
Write-Host "Результат: $OutputDir"
Write-Host ""
$Url = Read-Host "Введите URL видео с YouTube"
if ([string]::IsNullOrWhiteSpace($Url)) {
Write-Host ""
Write-Host "ОШИБКА: ссылка не введена." -ForegroundColor Red
Read-Host "Нажмите Enter для выхода"
exit 1
}
$OutputTemplate = Join-Path $OutputDir "%(title)s.%(ext)s"
Write-Host ""
Write-Host "Скачивание видео в максимально доступном качестве..."
Write-Host ""
& $YtDlp `
"-f" "bv*+ba/b" `
"--ffmpeg-location" $FfmpegDir `
"-o" $OutputTemplate `
$Url
if ($LASTEXITCODE -ne 0) {
Write-Host ""
Write-Host "ОШИБКА: yt-dlp завершил работу с кодом $LASTEXITCODE." -ForegroundColor Red
Read-Host "Нажмите Enter для выхода"
exit $LASTEXITCODE
}
Write-Host ""
Write-Host "Готово." -ForegroundColor Green
Write-Host "Файл сохранён в: $OutputDir"
Write-Host ""
Read-Host "Нажмите Enter для выхода"
+245
View File
@@ -0,0 +1,245 @@
#requires -Version 5.1
$ErrorActionPreference = "Stop"
# Корень проекта определяется независимо от текущей папки PowerShell.
$ProjectRoot = Split-Path -Parent $PSScriptRoot
$ToolsDir = Join-Path $ProjectRoot "tools"
$OutputDir = Join-Path $ProjectRoot "output"
if (-not (Test-Path $OutputDir)) {
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
}
function Find-Tool {
param(
[Parameter(Mandatory = $true)]
[string]$Name,
[string[]]$LocalNames = @()
)
foreach ($localName in $LocalNames) {
$localPath = Join-Path $ToolsDir $localName
if (Test-Path $localPath -PathType Leaf) {
return (Resolve-Path $localPath).Path
}
}
$command = Get-Command $Name -ErrorAction SilentlyContinue
if ($command) {
return $command.Source
}
return $null
}
function Require-Tool {
param(
[Parameter(Mandatory = $true)]
[string]$Name,
[string[]]$LocalNames = @(),
[string]$InstallHint = ""
)
$tool = Find-Tool -Name $Name -LocalNames $LocalNames
if (-not $tool) {
Write-Host ""
Write-Host "ОШИБКА: не найдена зависимость '$Name'." -ForegroundColor Red
Write-Host "Проверены:"
Write-Host " 1. Папка: $ToolsDir"
Write-Host " 2. Системная переменная PATH"
if ($InstallHint) {
Write-Host ""
Write-Host $InstallHint
}
Write-Host ""
Read-Host "Нажмите Enter для выхода"
exit 1
}
return $tool
}
$YtDlp = Require-Tool `
-Name "yt-dlp" `
-LocalNames @("yt-dlp.exe", "yt-dlp.cmd", "yt-dlp.ps1") `
-InstallHint "Скачать yt-dlp: https://github.com/yt-dlp/yt-dlp/releases/latest"
$Ffmpeg = Require-Tool `
-Name "ffmpeg" `
-LocalNames @("ffmpeg.exe") `
-InstallHint "Скачать FFmpeg: https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full.7z"
$VotCli = Require-Tool `
-Name "vot-cli" `
-LocalNames @("vot-cli.cmd", "vot-cli.exe", "vot-cli.ps1") `
-InstallHint "Установка vot-cli: npm install -g vot-cli"
$Node = Require-Tool `
-Name "node" `
-LocalNames @("node.exe") `
-InstallHint "Установите Node.js: https://nodejs.org/"
$Npm = Require-Tool `
-Name "npm" `
-LocalNames @("npm.cmd", "npm.exe", "npm.ps1") `
-InstallHint "npm устанавливается вместе с Node.js."
$FfmpegDir = Split-Path -Parent $Ffmpeg
function Get-SafeFileName {
param([string]$Name)
$invalidChars = [System.IO.Path]::GetInvalidFileNameChars()
foreach ($char in $invalidChars) {
$Name = $Name.Replace([string]$char, "_")
}
return $Name.Trim()
}
function Process-Video {
param(
[Parameter(Mandatory = $true)]
[string]$VideoLink,
[double]$OriginalSoundRatio = 0.2
)
$TempRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("youtube-tools-" + [Guid]::NewGuid().ToString("N"))
$TempVideoDir = Join-Path $TempRoot "video"
$TempAudioDir = Join-Path $TempRoot "audio"
New-Item -ItemType Directory -Path $TempVideoDir -Force | Out-Null
New-Item -ItemType Directory -Path $TempAudioDir -Force | Out-Null
try {
Write-Host ""
Write-Host "Получение названия видео..."
$Title = (& $YtDlp "--get-title" "--no-playlist" $VideoLink | Select-Object -First 1)
if ([string]::IsNullOrWhiteSpace($Title)) {
$Title = "YouTubeVideo"
}
$SafeTitle = Get-SafeFileName $Title
$TempVideoTemplate = Join-Path $TempVideoDir "%(title)s.%(ext)s"
Write-Host "Скачивание исходного видео..."
& $YtDlp `
"--no-playlist" `
"-f" "bv*+ba/b" `
"--ffmpeg-location" $FfmpegDir `
"-o" $TempVideoTemplate `
$VideoLink
if ($LASTEXITCODE -ne 0) {
throw "yt-dlp завершил работу с кодом $LASTEXITCODE."
}
$VideoFile = Get-ChildItem -Path $TempVideoDir -File |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1
if (-not $VideoFile) {
throw "Не найден скачанный видеофайл."
}
Write-Host "Получение переведённой аудиодорожки..."
& $VotCli $VideoLink "--output" $TempAudioDir
if ($LASTEXITCODE -ne 0) {
throw "vot-cli завершил работу с кодом $LASTEXITCODE."
}
$AudioFile = Get-ChildItem -Path $TempAudioDir -File |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1
if (-not $AudioFile) {
throw "vot-cli не создал аудиофайл."
}
$OutputFile = Join-Path $OutputDir ($SafeTitle + "_перевод.mp4")
Write-Host "Смешивание оригинального звука и перевода..."
$Filter = "[0:a]volume=$OriginalSoundRatio[original];[original][1:a]amix=inputs=2:duration=longest[audio_out]"
& $Ffmpeg `
"-hide_banner" `
"-i" $VideoFile.FullName `
"-i" $AudioFile.FullName `
"-c:v" "copy" `
"-c:a" "aac" `
"-b:a" "128k" `
"-filter_complex" $Filter `
"-map" "0:v:0" `
"-map" "[audio_out]" `
"-movflags" "+faststart" `
"-y" `
$OutputFile
if ($LASTEXITCODE -ne 0) {
throw "FFmpeg завершил работу с кодом $LASTEXITCODE."
}
Write-Host ""
Write-Host "Готово: $OutputFile" -ForegroundColor Green
}
finally {
if (Test-Path $TempRoot) {
Remove-Item -Path $TempRoot -Recurse -Force -ErrorAction SilentlyContinue
}
}
}
Write-Host ""
Write-Host "============================================================"
Write-Host " СКАЧИВАНИЕ YOUTUBE С ЗАКАДРОВЫМ ПЕРЕВОДОМ"
Write-Host "============================================================"
Write-Host "Результат: $OutputDir"
Write-Host ""
$VideoLinksInput = Read-Host "Введите ссылку или несколько ссылок через запятую"
if ([string]::IsNullOrWhiteSpace($VideoLinksInput)) {
Write-Host ""
Write-Host "ОШИБКА: ссылки не введены." -ForegroundColor Red
Read-Host "Нажмите Enter для выхода"
exit 1
}
$VideoLinks = $VideoLinksInput -split "," |
ForEach-Object { $_.Trim() } |
Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
foreach ($VideoLink in $VideoLinks) {
Write-Host ""
Write-Host "------------------------------------------------------------"
Write-Host "Обработка: $VideoLink"
Write-Host "------------------------------------------------------------"
try {
Process-Video -VideoLink $VideoLink -OriginalSoundRatio 0.2
}
catch {
Write-Host ""
Write-Host "ОШИБКА: $($_.Exception.Message)" -ForegroundColor Red
}
}
Write-Host ""
Write-Host "Обработка завершена."
Write-Host "Результаты находятся в: $OutputDir"
Write-Host ""
Read-Host "Нажмите Enter для выхода"
View File