Add project files
This commit is contained in:
194
Program.cs
Normal file
194
Program.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using PdfSharp.Drawing;
|
||||
using PdfSharp.Pdf;
|
||||
|
||||
namespace WSiPBookDownloader;
|
||||
|
||||
internal class BookMissingException : Exception
|
||||
{
|
||||
public int BookId { get; }
|
||||
|
||||
public BookMissingException(int bookId)
|
||||
: base($"Book with ID {bookId} does not exist.")
|
||||
{
|
||||
BookId = bookId;
|
||||
}
|
||||
}
|
||||
|
||||
internal class Program
|
||||
{
|
||||
private static readonly HttpClient httpClient = new
|
||||
(
|
||||
new HttpClientHandler
|
||||
{
|
||||
AllowAutoRedirect = false,
|
||||
}
|
||||
)
|
||||
{
|
||||
BaseAddress = new Uri("https://appwsipnet.eduranga.pl/"),
|
||||
};
|
||||
|
||||
private static readonly Lock _consoleLock = new();
|
||||
|
||||
private static void Log(string message, ConsoleColor? color = null)
|
||||
{
|
||||
lock (_consoleLock)
|
||||
{
|
||||
if (color.HasValue)
|
||||
Console.ForegroundColor = color.Value;
|
||||
|
||||
Console.WriteLine(message);
|
||||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
if (args.Length < 2 || args.Length > 4)
|
||||
{
|
||||
Log("Usage: [outputDirectory] [startBookId] [endBookId] [--only-covers]", ConsoleColor.DarkRed);
|
||||
return;
|
||||
}
|
||||
|
||||
var outputDirectory = args[0];
|
||||
if (!Path.Exists(outputDirectory))
|
||||
{
|
||||
Log($"Output directory \"{outputDirectory}\" does not exist.", ConsoleColor.DarkRed);
|
||||
return;
|
||||
}
|
||||
|
||||
var startBookId = int.Parse(args[1]);
|
||||
var endBookId = (args.Length >= 3 && int.TryParse(args[2], out var parsedEndBookId))
|
||||
? parsedEndBookId
|
||||
: startBookId;
|
||||
var onlyCovers = args.FirstOrDefault(arg => arg.Equals("--only-covers", StringComparison.OrdinalIgnoreCase)) != null;
|
||||
|
||||
var bookIds = Enumerable.Range(startBookId, endBookId - startBookId + 1);
|
||||
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = onlyCovers ? 100 : 10 };
|
||||
|
||||
await Parallel.ForEachAsync(bookIds, parallelOptions, async (bookId, cancellationToken) =>
|
||||
{
|
||||
var outputPath = Path.Combine(outputDirectory, $"{bookId}.pdf");
|
||||
if (File.Exists(outputPath))
|
||||
{
|
||||
Log($"[!] Book with ID {bookId} already downloaded, skipping...", ConsoleColor.Yellow);
|
||||
return;
|
||||
}
|
||||
|
||||
Log($"[?] Starting download of book with ID {bookId}", ConsoleColor.Cyan);
|
||||
var jpgDir = Path.Combine(Path.GetTempPath(), "WSiPBookDownloader", bookId.ToString());
|
||||
|
||||
try
|
||||
{
|
||||
if (onlyCovers)
|
||||
{
|
||||
await DownloadBookCoverJpg(bookId, outputDirectory, cancellationToken);
|
||||
Log($"[✓] Downloaded book cover {bookId}", ConsoleColor.DarkGreen);
|
||||
return;
|
||||
}
|
||||
|
||||
await DownloadBookJpg(bookId, jpgDir, cancellationToken);
|
||||
BuildPdf(bookId, jpgDir, outputPath);
|
||||
|
||||
Log($"[✓] Downloaded book {bookId}", ConsoleColor.DarkGreen);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[×] Failed to download book {bookId}:\n {ex.Message}", ConsoleColor.DarkRed);
|
||||
} finally
|
||||
{
|
||||
if (Directory.Exists(jpgDir))
|
||||
Directory.Delete(jpgDir, recursive: true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static async Task DownloadBookCoverJpg(int bookId, string outputDir, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var outputPath = Path.Combine(outputDir, $"{bookId}.jpg");
|
||||
if (File.Exists(outputPath))
|
||||
{
|
||||
Debug.WriteLine($"Cover for book {bookId} already exists, skipping...");
|
||||
return;
|
||||
}
|
||||
|
||||
var response = await httpClient.GetAsync($"e-podreczniki/podglad/{bookId}/files/mobile/1.jpg", cancellationToken);
|
||||
if (response.StatusCode == HttpStatusCode.NotFound || response.StatusCode == HttpStatusCode.Found)
|
||||
throw new BookMissingException(bookId);
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var content = await response.Content.ReadAsByteArrayAsync(cancellationToken);
|
||||
Directory.CreateDirectory(outputDir);
|
||||
await File.WriteAllBytesAsync(outputPath, content, cancellationToken);
|
||||
|
||||
Debug.WriteLine($"Downloaded cover for book {bookId}");
|
||||
}
|
||||
|
||||
private static async Task DownloadBookJpg(int bookId, string outputDir, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var currentPage = 1;
|
||||
while (true)
|
||||
{
|
||||
var outputPath = Path.Combine(outputDir, $"{currentPage}.jpg");
|
||||
if (File.Exists(outputPath))
|
||||
{
|
||||
Debug.WriteLine($"Page {currentPage} of book {bookId} already exists, skipping...");
|
||||
|
||||
currentPage++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var response = await httpClient.GetAsync($"e-podreczniki/podglad/{bookId}/files/mobile/{currentPage}.jpg", cancellationToken);
|
||||
if (
|
||||
(response.StatusCode == HttpStatusCode.Found || response.StatusCode == HttpStatusCode.NotFound) &&
|
||||
currentPage > 1
|
||||
)
|
||||
{
|
||||
Debug.WriteLine($"No more pages for book {bookId}, stopping...");
|
||||
break;
|
||||
} else if (response.StatusCode == HttpStatusCode.NotFound || response.StatusCode == HttpStatusCode.Found)
|
||||
throw new BookMissingException(bookId);
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var content = await response.Content.ReadAsByteArrayAsync(cancellationToken);
|
||||
Directory.CreateDirectory(outputDir);
|
||||
await File.WriteAllBytesAsync(outputPath, content, cancellationToken);
|
||||
|
||||
Debug.WriteLine($"Downloaded page {currentPage} of book {bookId}");
|
||||
|
||||
currentPage++;
|
||||
}
|
||||
}
|
||||
|
||||
private static void BuildPdf(int bookId, string jpgDir, string outputPath)
|
||||
{
|
||||
var pages = Directory.GetFiles(jpgDir, "*.jpg")
|
||||
.OrderBy(file => int.Parse(Path.GetFileNameWithoutExtension(file)))
|
||||
.ToList();
|
||||
|
||||
using var document = new PdfDocument();
|
||||
document.Options.FlateEncodeMode = PdfFlateEncodeMode.BestCompression;
|
||||
document.Options.UseFlateDecoderForJpegImages = PdfUseFlateDecoderForJpegImages.Never;
|
||||
|
||||
foreach (var pagePath in pages)
|
||||
{
|
||||
using var stream = new MemoryStream(File.ReadAllBytes(pagePath));
|
||||
using var image = XImage.FromStream(stream);
|
||||
|
||||
var pdfPage = document.AddPage();
|
||||
pdfPage.Width = XUnit.FromPoint(image.PointWidth);
|
||||
pdfPage.Height = XUnit.FromPoint(image.PointHeight);
|
||||
|
||||
using var gfx = XGraphics.FromPdfPage(pdfPage);
|
||||
gfx.DrawImage(image, 0, 0, image.PointWidth, image.PointHeight);
|
||||
|
||||
Debug.WriteLine($"Added page {Path.GetFileNameWithoutExtension(pagePath)} to PDF for book {bookId}");
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!);
|
||||
document.Save(outputPath);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user