Skip to content

Commit 336970f

Browse files
committed
Updates on downloader console
1 parent 5ba7d52 commit 336970f

File tree

3 files changed

+145
-75
lines changed

3 files changed

+145
-75
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System;
2+
using System.IO;
3+
using Koware.Domain.Models;
4+
5+
namespace Koware.Cli.Console;
6+
7+
internal static class DownloadConsole
8+
{
9+
public static void PrintEpisodeHeader(string title, Episode episode, string? quality, int index, int total, string outputPath)
10+
{
11+
System.Console.WriteLine();
12+
System.Console.ForegroundColor = ConsoleColor.Cyan;
13+
System.Console.WriteLine($"Downloading {title} - Ep {episode.Number} [{quality ?? "auto"}] ({index}/{total})");
14+
System.Console.ResetColor();
15+
System.Console.WriteLine($" -> {outputPath}");
16+
}
17+
18+
public static void PrintEpisodeResult(string outputPath, int episodesLeft)
19+
{
20+
double sizeMb = 0;
21+
22+
try
23+
{
24+
if (File.Exists(outputPath))
25+
{
26+
var length = new FileInfo(outputPath).Length;
27+
sizeMb = length / (1024.0 * 1024.0);
28+
}
29+
}
30+
catch
31+
{
32+
}
33+
34+
if (sizeMb > 0)
35+
{
36+
System.Console.WriteLine($" Downloaded size: {sizeMb:0.0} MiB");
37+
}
38+
else
39+
{
40+
System.Console.WriteLine(" Download completed.");
41+
}
42+
43+
if (episodesLeft > 0)
44+
{
45+
System.Console.WriteLine($" Episodes left to download: {episodesLeft}");
46+
}
47+
}
48+
}

Koware.Cli/Program.cs

Lines changed: 61 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ static IHost BuildHost(string[] args)
3939
builder.Services.AddSingleton<IWatchHistoryStore, SqliteWatchHistoryStore>();
4040
builder.Logging.SetMinimumLevel(LogLevel.Warning);
4141
builder.Logging.AddFilter("koware", LogLevel.Information);
42-
builder.Logging.AddFilter("Koware", LogLevel.Information);
4342
builder.Logging.AddFilter("Koware.Infrastructure.Scraping.GogoAnimeCatalog", LogLevel.Error);
43+
builder.Logging.AddFilter("Koware.Infrastructure.Scraping.AllAnimeCatalog", LogLevel.Error);
4444

4545
return builder.Build();
4646
}
@@ -918,10 +918,7 @@ static async Task<int> HandleDownloadAsync(ScrapeOrchestrator orchestrator, stri
918918
: allAnimeOptions?.Referer;
919919
var httpUserAgent = allAnimeOptions?.UserAgent;
920920

921-
Console.WriteLine();
922-
Console.ForegroundColor = ConsoleColor.Cyan;
923-
Console.WriteLine($"Downloading {title} - Ep {episode.Number} [{stream.Quality ?? "auto"}] ({index}/{total})");
924-
Console.ResetColor();
921+
DownloadConsole.PrintEpisodeHeader(title, episode, stream.Quality, index, total, outputPath);
925922

926923
var isPlaylist = IsPlaylist(stream);
927924
var ffmpegPath = ResolveExecutablePath("ffmpeg");
@@ -940,6 +937,9 @@ static async Task<int> HandleDownloadAsync(ScrapeOrchestrator orchestrator, stri
940937
{
941938
await DownloadWithHttpAsync(httpClient, stream, outputPath, httpReferrer, httpUserAgent, logger, cancellationToken);
942939
}
940+
941+
var episodesLeft = total - index;
942+
DownloadConsole.PrintEpisodeResult(outputPath, episodesLeft);
943943
}
944944
catch (OperationCanceledException)
945945
{
@@ -1267,56 +1267,11 @@ static async Task DownloadWithHttpAsync(HttpClient httpClient, StreamLink stream
12671267
await using var fileStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.None, 81920, useAsync: true);
12681268

12691269
var buffer = new byte[81920];
1270-
long totalRead = 0;
12711270
int read;
1272-
var lastUpdate = DateTimeOffset.UtcNow;
12731271

12741272
while ((read = await responseStream.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellationToken)) > 0)
12751273
{
12761274
await fileStream.WriteAsync(buffer.AsMemory(0, read), cancellationToken);
1277-
totalRead += read;
1278-
1279-
var now = DateTimeOffset.UtcNow;
1280-
if (now - lastUpdate > TimeSpan.FromMilliseconds(100))
1281-
{
1282-
lastUpdate = now;
1283-
RenderDownloadProgress(totalRead, total);
1284-
}
1285-
}
1286-
1287-
RenderDownloadProgress(totalRead, total);
1288-
Console.WriteLine();
1289-
1290-
static void RenderDownloadProgress(long bytesRead, long? totalBytes)
1291-
{
1292-
var readMb = bytesRead / (1024.0 * 1024.0);
1293-
string text;
1294-
1295-
if (totalBytes.HasValue && totalBytes.Value > 0)
1296-
{
1297-
var totalMb = totalBytes.Value / (1024.0 * 1024.0);
1298-
var progress = Math.Clamp(bytesRead / (double)totalBytes.Value, 0, 1);
1299-
const int width = 30;
1300-
var filled = (int)(progress * width);
1301-
if (filled > width)
1302-
{
1303-
filled = width;
1304-
}
1305-
1306-
var bar = new string('#', filled) + new string('-', width - filled);
1307-
text = $"[{bar}] {progress * 100,5:0.0}% {readMb,6:0.0}/{totalMb,6:0.0} MiB";
1308-
}
1309-
else
1310-
{
1311-
text = $"Downloaded {readMb:0.0} MiB";
1312-
}
1313-
1314-
if (text.Length > 80)
1315-
{
1316-
text = text[..80];
1317-
}
1318-
1319-
Console.Write("\r" + text.PadRight(80));
13201275
}
13211276
}
13221277

@@ -1342,41 +1297,72 @@ static string BuildFfmpegHeaders(string? httpReferrer, string? httpUserAgent)
13421297
return string.Join("\r\n", parts) + "\r\n";
13431298
}
13441299

1345-
static Task DownloadWithFfmpegAsync(string ffmpegPath, StreamLink stream, string outputPath, string? httpReferrer, string? httpUserAgent, ILogger logger, CancellationToken cancellationToken)
1300+
static async Task DownloadWithFfmpegAsync(string ffmpegPath, StreamLink stream, string outputPath, string? httpReferrer, string? httpUserAgent, ILogger logger, CancellationToken cancellationToken)
13461301
{
1347-
return Task.Run(() =>
1302+
cancellationToken.ThrowIfCancellationRequested();
1303+
1304+
var start = new ProcessStartInfo
13481305
{
1349-
cancellationToken.ThrowIfCancellationRequested();
1306+
FileName = ffmpegPath,
1307+
UseShellExecute = false,
1308+
RedirectStandardError = true,
1309+
RedirectStandardOutput = true,
1310+
CreateNoWindow = true
1311+
};
13501312

1351-
var start = new ProcessStartInfo
1352-
{
1353-
FileName = ffmpegPath,
1354-
UseShellExecute = false
1355-
};
1313+
start.ArgumentList.Add("-y");
1314+
start.ArgumentList.Add("-loglevel");
1315+
start.ArgumentList.Add("error");
1316+
1317+
var headers = BuildFfmpegHeaders(httpReferrer, httpUserAgent);
1318+
if (!string.IsNullOrWhiteSpace(headers))
1319+
{
1320+
start.ArgumentList.Add("-headers");
1321+
start.ArgumentList.Add(headers);
1322+
}
13561323

1357-
start.ArgumentList.Add("-y");
1324+
start.ArgumentList.Add("-i");
1325+
start.ArgumentList.Add(stream.Url.ToString());
1326+
start.ArgumentList.Add("-c");
1327+
start.ArgumentList.Add("copy");
1328+
start.ArgumentList.Add("-bsf:a");
1329+
start.ArgumentList.Add("aac_adtstoasc");
1330+
start.ArgumentList.Add(outputPath);
13581331

1359-
var headers = BuildFfmpegHeaders(httpReferrer, httpUserAgent);
1360-
if (!string.IsNullOrWhiteSpace(headers))
1332+
using var process = new Process { StartInfo = start };
1333+
1334+
try
1335+
{
1336+
if (!process.Start())
13611337
{
1362-
start.ArgumentList.Add("-headers");
1363-
start.ArgumentList.Add(headers);
1338+
throw new InvalidOperationException("Failed to start ffmpeg process.");
13641339
}
13651340

1366-
start.ArgumentList.Add("-i");
1367-
start.ArgumentList.Add(stream.Url.ToString());
1368-
start.ArgumentList.Add("-c");
1369-
start.ArgumentList.Add("copy");
1370-
start.ArgumentList.Add("-bsf:a");
1371-
start.ArgumentList.Add("aac_adtstoasc");
1372-
start.ArgumentList.Add(outputPath);
1373-
1374-
var exitCode = StartProcessAndWait(logger, start, ffmpegPath);
1375-
if (exitCode != 0)
1341+
_ = process.StandardError.ReadToEndAsync();
1342+
_ = process.StandardOutput.ReadToEndAsync();
1343+
1344+
await Task.Run(() => process.WaitForExit(), cancellationToken);
1345+
1346+
if (process.ExitCode != 0)
13761347
{
1377-
throw new InvalidOperationException($"ffmpeg exited with code {exitCode}.");
1348+
throw new InvalidOperationException($"ffmpeg exited with code {process.ExitCode}.");
13781349
}
1379-
}, cancellationToken);
1350+
}
1351+
catch
1352+
{
1353+
try
1354+
{
1355+
if (!process.HasExited)
1356+
{
1357+
process.Kill(entireProcessTree: true);
1358+
}
1359+
}
1360+
catch
1361+
{
1362+
}
1363+
1364+
throw;
1365+
}
13801366
}
13811367

13821368
static string? ResolveExecutablePath(string command)

Scripts/test-koware.ps1

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
[CmdletBinding()]
2+
param(
3+
[ValidateSet("Debug", "Release")]
4+
[string]$Configuration = "Debug"
5+
)
6+
7+
$ErrorActionPreference = "Stop"
8+
9+
# Determine repo root (this script lives in the Scripts folder)
10+
$repoRoot = Split-Path -Path $PSScriptRoot -Parent
11+
$testProject = Join-Path $repoRoot "Koware.Tests\Koware.Tests.csproj"
12+
13+
if (-not (Test-Path $testProject)) {
14+
Write-Host "Test project not found: $testProject" -ForegroundColor Red
15+
exit 1
16+
}
17+
18+
Write-Host "Running Koware tests (configuration: $Configuration)..." -ForegroundColor Cyan
19+
20+
Push-Location $repoRoot
21+
try {
22+
dotnet test $testProject -c $Configuration --nologo
23+
$exitCode = $LASTEXITCODE
24+
}
25+
finally {
26+
Pop-Location
27+
}
28+
29+
if ($exitCode -eq 0) {
30+
Write-Host "All tests passed." -ForegroundColor Green
31+
exit 0
32+
}
33+
else {
34+
Write-Host "One or more tests failed. dotnet test exit code: $exitCode" -ForegroundColor Red
35+
exit $exitCode
36+
}

0 commit comments

Comments
 (0)