Релиз v1.0.0

This commit is contained in:
2026-08-09 12:06:46 +03:00
commit c3b3ce9901
14 changed files with 2207 additions and 0 deletions
+40
View File
@@ -0,0 +1,40 @@
@echo off
chcp 65001 >nul
setlocal
set "STREAMRIP_DIR=%APPDATA%\streamrip"
set "DOWNLOADS_DB=%STREAMRIP_DIR%\downloads.db"
set "FAILED_DB=%STREAMRIP_DIR%\failed_downloads.db"
echo.
echo ============================================
echo ОЧИСТКА БАЗЫ STREAMRIP
echo ============================================
echo.
if exist "%DOWNLOADS_DB%" (
del /q "%DOWNLOADS_DB%" >nul 2>&1
if exist "%DOWNLOADS_DB%" (
echo ОШИБКА: не удалось удалить downloads.db
) else (
echo downloads.db удалён.
)
) else (
echo downloads.db не найден — пропущено.
)
if exist "%FAILED_DB%" (
del /q "%FAILED_DB%" >nul 2>&1
if exist "%FAILED_DB%" (
echo ОШИБКА: не удалось удалить failed_downloads.db
) else (
echo failed_downloads.db удалён.
)
) else (
echo failed_downloads.db не найден — пропущено.
)
echo.
echo Готово.
echo.
pause
+510
View File
@@ -0,0 +1,510 @@
#requires -Version 5.1
<#
Конвертация всех FLAC-файлов в CD-качество:
16 бит / 44,1 кГц / стерео.
Скрипт:
- рекурсивно ищет все *.flac;
- пропускает файлы, которые уже 16 бит / 44100 Гц / стерео;
- сохраняет теги и встроенную обложку;
- проверяет результат через ffprobe;
- после успешной проверки заменяет исходный файл;
- показывает прогресс;
- в конце выводит список файлов с ошибками.
#>
$ProjectRoot = Split-Path -Parent $PSScriptRoot
$ToolsFolder = Join-Path $ProjectRoot "tools"
$RootFolder = Join-Path $ProjectRoot "downloads"
$ErrorActionPreference = "Stop"
function Find-Tool {
param(
[Parameter(Mandatory = $true)]
[string]$CommandName,
[Parameter(Mandatory = $true)]
[string[]]$LocalNames
)
foreach ($LocalName in $LocalNames) {
$LocalPath = Join-Path $ToolsFolder $LocalName
if (Test-Path -LiteralPath $LocalPath -PathType Leaf) {
return (Resolve-Path -LiteralPath $LocalPath).Path
}
}
$Command = Get-Command $CommandName -ErrorAction SilentlyContinue
if ($Command) {
return $Command.Source
}
return $null
}
$FfmpegPath = Find-Tool -CommandName "ffmpeg.exe" -LocalNames @("ffmpeg.exe")
$FfprobePath = Find-Tool -CommandName "ffprobe.exe" -LocalNames @("ffprobe.exe")
try {
$Host.UI.RawUI.WindowTitle = "FLAC -> CD quality"
}
catch {
}
function Wait-BeforeExit {
Write-Host ""
[void](Read-Host "Нажмите Enter для выхода")
}
function Get-FlacAudioInfo {
param(
[Parameter(Mandatory = $true)]
[string]$Path
)
$probeOutput = & $FfprobePath `
-v error `
-select_streams a:0 `
-show_entries "stream=codec_name,sample_rate,channels,bits_per_sample,bits_per_raw_sample,sample_fmt" `
-of json `
-- `
$Path 2>&1
if ($LASTEXITCODE -ne 0) {
throw "ffprobe не смог прочитать файл:`n$probeOutput"
}
$probeData = $probeOutput | ConvertFrom-Json
if (
-not $probeData.streams -or
$probeData.streams.Count -lt 1
) {
throw "Аудиопоток не найден."
}
$stream = $probeData.streams[0]
$bitDepth = 0
if (
$null -ne $stream.bits_per_raw_sample -and
[string]$stream.bits_per_raw_sample -ne "" -and
[int]$stream.bits_per_raw_sample -gt 0
) {
$bitDepth = [int]$stream.bits_per_raw_sample
}
elseif (
$null -ne $stream.bits_per_sample -and
[string]$stream.bits_per_sample -ne "" -and
[int]$stream.bits_per_sample -gt 0
) {
$bitDepth = [int]$stream.bits_per_sample
}
elseif ([string]$stream.sample_fmt -match "s16") {
$bitDepth = 16
}
elseif ([string]$stream.sample_fmt -match "s32") {
$bitDepth = 24
}
[PSCustomObject]@{
Codec = [string]$stream.codec_name
SampleRate = [int]$stream.sample_rate
Channels = [int]$stream.channels
BitDepth = [int]$bitDepth
SampleFmt = [string]$stream.sample_fmt
}
}
Clear-Host
Write-Host "FLAC -> CD quality" -ForegroundColor Cyan
Write-Host "16 бит / 44,1 кГц / стерео"
Write-Host "Папка: $RootFolder"
Write-Host ""
if (
-not (
Test-Path `
-LiteralPath $RootFolder `
-PathType Container
)
) {
Write-Host "ОШИБКА: папка не найдена:" -ForegroundColor Red
Write-Host $RootFolder -ForegroundColor Red
Wait-BeforeExit
exit 1
}
if (-not $FfmpegPath) {
Write-Host "ОШИБКА: ffmpeg.exe не найден." -ForegroundColor Red
Write-Host "Проверены папка tools и системный PATH."
Wait-BeforeExit
exit 1
}
if (-not $FfprobePath) {
Write-Host "ОШИБКА: ffprobe.exe не найден." -ForegroundColor Red
Write-Host "Проверены папка tools и системный PATH."
Wait-BeforeExit
exit 1
}
$files = @(
Get-ChildItem `
-LiteralPath $RootFolder `
-Filter "*.flac" `
-File `
-Recurse |
Where-Object {
$_.Name -notlike "*.__cd_temp__.flac"
} |
Sort-Object FullName
)
$total = $files.Count
if ($total -eq 0) {
Write-Host "FLAC-файлы не найдены." `
-ForegroundColor Yellow
Wait-BeforeExit
exit 0
}
$converted = 0
$skipped = 0
$failed = 0
$index = 0
$startTime = Get-Date
$failedFiles = [System.Collections.Generic.List[object]]::new()
foreach ($file in $files) {
$index++
$percent = [int][Math]::Floor(
(($index - 1) / $total) * 100
)
Write-Progress `
-Activity "Конвертация FLAC в CD-качество" `
-Status (
"Файл {0} из {1}: {2}" -f `
$index,
$total,
$file.Name
) `
-CurrentOperation $file.DirectoryName `
-PercentComplete $percent
Write-Host ""
Write-Host (
"[{0}/{1}] {2}" -f `
$index,
$total,
$file.FullName
) -ForegroundColor White
$tempFile = Join-Path `
-Path $file.DirectoryName `
-ChildPath (
$file.BaseName +
".__cd_temp__.flac"
)
$backupFile = Join-Path `
-Path $file.DirectoryName `
-ChildPath (
$file.Name +
".__original_backup__"
)
try {
if (Test-Path -LiteralPath $tempFile) {
Remove-Item `
-LiteralPath $tempFile `
-Force
}
if (Test-Path -LiteralPath $backupFile) {
Remove-Item `
-LiteralPath $backupFile `
-Force
}
$sourceInfo = Get-FlacAudioInfo `
-Path $file.FullName
Write-Host (
"Исходник: {0} бит, {1} Гц, {2} канал(а)" -f `
$sourceInfo.BitDepth,
$sourceInfo.SampleRate,
$sourceInfo.Channels
)
$alreadyCdQuality = (
$sourceInfo.BitDepth -eq 16 -and
$sourceInfo.SampleRate -eq 44100 -and
$sourceInfo.Channels -eq 2
)
if ($alreadyCdQuality) {
$skipped++
Write-Host `
"Уже CD-качество — пропущено." `
-ForegroundColor Green
continue
}
Write-Host "Конвертация..." `
-ForegroundColor Yellow
$ffmpegOutput = & $FfmpegPath `
-hide_banner `
-loglevel error `
-y `
-i `
$file.FullName `
-map 0:a:0 `
-map "0:v?" `
-map_metadata 0 `
-c:a flac `
-sample_fmt s16 `
-ar 44100 `
-ac 2 `
-compression_level 8 `
-c:v copy `
-- `
$tempFile 2>&1
if ($LASTEXITCODE -ne 0) {
throw "ffmpeg завершился с ошибкой:`n$ffmpegOutput"
}
if (
-not (
Test-Path `
-LiteralPath $tempFile `
-PathType Leaf
)
) {
throw "Временный файл не был создан."
}
$tempFileInfo = Get-Item `
-LiteralPath $tempFile
if ($tempFileInfo.Length -le 0) {
throw "Создан пустой временный файл."
}
$resultInfo = Get-FlacAudioInfo `
-Path $tempFile
$validResult = (
$resultInfo.BitDepth -eq 16 -and
$resultInfo.SampleRate -eq 44100 -and
$resultInfo.Channels -eq 2
)
if (-not $validResult) {
throw (
"Проверка результата не пройдена. " +
"Получено: {0} бит, {1} Гц, " +
"{2} канал(а)." -f `
$resultInfo.BitDepth,
$resultInfo.SampleRate,
$resultInfo.Channels
)
}
$originalCreationTime = $file.CreationTime
$originalLastWriteTime = $file.LastWriteTime
Move-Item `
-LiteralPath $file.FullName `
-Destination $backupFile
try {
Move-Item `
-LiteralPath $tempFile `
-Destination $file.FullName
$newFile = Get-Item `
-LiteralPath $file.FullName
$newFile.CreationTime = $originalCreationTime
$newFile.LastWriteTime = $originalLastWriteTime
Remove-Item `
-LiteralPath $backupFile `
-Force
}
catch {
if (
Test-Path `
-LiteralPath $file.FullName
) {
Remove-Item `
-LiteralPath $file.FullName `
-Force `
-ErrorAction SilentlyContinue
}
if (
Test-Path `
-LiteralPath $backupFile
) {
Move-Item `
-LiteralPath $backupFile `
-Destination $file.FullName `
-ErrorAction SilentlyContinue
}
throw
}
$converted++
Write-Host `
"Готово — исходный файл заменён." `
-ForegroundColor Cyan
}
catch {
$failed++
$errorMessage = $_.Exception.Message
$failedFiles.Add(
[PSCustomObject]@{
Name = $file.BaseName
FileName = $file.Name
FullName = $file.FullName
Directory = $file.DirectoryName
Error = $errorMessage
}
)
Write-Host "ОШИБКА:" `
-ForegroundColor Red
Write-Host $errorMessage `
-ForegroundColor Red
if (
Test-Path `
-LiteralPath $tempFile
) {
Remove-Item `
-LiteralPath $tempFile `
-Force `
-ErrorAction SilentlyContinue
}
if (
(Test-Path -LiteralPath $backupFile) -and
-not (
Test-Path `
-LiteralPath $file.FullName
)
) {
Move-Item `
-LiteralPath $backupFile `
-Destination $file.FullName `
-ErrorAction SilentlyContinue
}
}
}
Write-Progress `
-Activity "Конвертация FLAC в CD-качество" `
-Completed
$elapsed = (Get-Date) - $startTime
Write-Host ""
Write-Host "========================================" `
-ForegroundColor DarkGray
Write-Host "Обработка завершена" `
-ForegroundColor Cyan
Write-Host (
"Всего файлов: {0}" -f $total
)
Write-Host (
"Конвертировано: {0}" -f $converted
) -ForegroundColor Cyan
Write-Host (
"Пропущено (уже CD): {0}" -f $skipped
) -ForegroundColor Green
if ($failed -gt 0) {
Write-Host (
"Ошибок: {0}" -f $failed
) -ForegroundColor Red
}
else {
Write-Host "Ошибок: 0" `
-ForegroundColor Gray
}
Write-Host (
"Время: {0:hh\:mm\:ss}" -f $elapsed
)
Write-Host "========================================" `
-ForegroundColor DarkGray
if ($failedFiles.Count -gt 0) {
Write-Host ""
Write-Host "ТРЕКИ С ОШИБКАМИ:" `
-ForegroundColor Red
Write-Host "========================================" `
-ForegroundColor DarkGray
$failedNumber = 0
foreach ($failedFile in $failedFiles) {
$failedNumber++
Write-Host (
"{0}. {1}" -f `
$failedNumber,
$failedFile.Name
) -ForegroundColor Red
Write-Host (
" Файл: {0}" -f `
$failedFile.FullName
) -ForegroundColor DarkGray
Write-Host (
" Ошибка: {0}" -f `
$failedFile.Error
) -ForegroundColor DarkYellow
Write-Host ""
}
Write-Host "========================================" `
-ForegroundColor DarkGray
}
else {
Write-Host ""
Write-Host "Все файлы обработаны без ошибок." `
-ForegroundColor Green
}
Wait-BeforeExit
+93
View File
@@ -0,0 +1,93 @@
#requires -Version 5.1
$ErrorActionPreference = "Stop"
$ProjectRoot = Split-Path -Parent $PSScriptRoot
$RootPath = Join-Path $ProjectRoot "downloads"
$Separator = " - "
try {
$Host.UI.RawUI.WindowTitle = "Группировка альбомов по исполнителям"
}
catch {
}
function Wait-BeforeExit {
Write-Host ""
[void](Read-Host "Нажмите Enter для выхода")
}
Clear-Host
Write-Host "Группировка альбомов по исполнителям" -ForegroundColor Cyan
Write-Host "Папка: $RootPath"
Write-Host ""
if (-not (Test-Path -LiteralPath $RootPath -PathType Container)) {
New-Item -ItemType Directory -Path $RootPath -Force | Out-Null
}
$AlbumFolders = Get-ChildItem -LiteralPath $RootPath -Directory |
Where-Object {
$_.Name.Contains($Separator)
} |
ForEach-Object {
$Artist = ($_.Name -split [regex]::Escape($Separator), 2)[0].Trim()
[PSCustomObject]@{
Artist = $Artist
Folder = $_
}
}
$ArtistGroups = $AlbumFolders | Group-Object -Property Artist
$Moved = 0
$Skipped = 0
foreach ($Group in $ArtistGroups) {
# Папка исполнителя создаётся только при наличии двух и более альбомов.
if ($Group.Count -lt 2) {
continue
}
$ArtistName = $Group.Name
$ArtistPath = Join-Path -Path $RootPath -ChildPath $ArtistName
if (-not (Test-Path -LiteralPath $ArtistPath -PathType Container)) {
New-Item -Path $ArtistPath -ItemType Directory | Out-Null
Write-Host "Создана папка: $ArtistName" -ForegroundColor Green
}
foreach ($Album in $Group.Group) {
$SourcePath = $Album.Folder.FullName
$DestinationPath = Join-Path -Path $ArtistPath -ChildPath $Album.Folder.Name
if (Test-Path -LiteralPath $DestinationPath) {
Write-Warning "Папка уже существует, пропущено: $DestinationPath"
$Skipped++
continue
}
try {
Move-Item `
-LiteralPath $SourcePath `
-Destination $ArtistPath `
-ErrorAction Stop
Write-Host "Перемещено: $($Album.Folder.Name) -> $ArtistName" -ForegroundColor Cyan
$Moved++
}
catch {
Write-Warning "Не удалось переместить '$SourcePath': $($_.Exception.Message)"
$Skipped++
}
}
}
Write-Host ""
Write-Host "Обработка завершена." -ForegroundColor Yellow
Write-Host "Перемещено: $Moved"
Write-Host "Пропущено: $Skipped"
Wait-BeforeExit