Files
BookReader/BookReader/Services/CalibreWebService.cs
Курнат Андрей 8cb459c832 qwen edit
2026-02-18 14:21:53 +03:00

142 lines
5.0 KiB
C#

using BookReader.Models;
using Newtonsoft.Json.Linq;
using System.Net.Http.Headers;
using System.Text;
namespace BookReader.Services;
public class CalibreWebService : ICalibreWebService
{
private readonly HttpClient _httpClient;
private string _baseUrl = string.Empty;
private string _username = string.Empty;
private string _password = string.Empty;
public CalibreWebService(HttpClient httpClient)
{
_httpClient = httpClient;
_httpClient.Timeout = TimeSpan.FromSeconds(Constants.Network.HttpClientTimeoutSeconds);
}
public void Configure(string url, string username, string password)
{
_baseUrl = url.TrimEnd('/');
_username = username;
_password = password;
if (!string.IsNullOrEmpty(_username))
{
var authBytes = Encoding.ASCII.GetBytes($"{_username}:{_password}");
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authBytes));
}
}
public async Task<bool> TestConnectionAsync(string url, string username, string password)
{
try
{
Configure(url, username, password);
var response = await _httpClient.GetAsync($"{_baseUrl}/ajax/search?query=&num=1");
return response.IsSuccessStatusCode;
}
catch
{
return false;
}
}
public async Task<List<CalibreBook>> GetBooksAsync(string? searchQuery = null, int page = 0, int pageSize = Constants.Network.CalibrePageSize)
{
var books = new List<CalibreBook>();
try
{
var offset = page * pageSize;
var query = string.IsNullOrEmpty(searchQuery) ? "" : Uri.EscapeDataString(searchQuery);
var url = $"{_baseUrl}/ajax/search?query={query}&num={pageSize}&offset={offset}&sort=timestamp&sort_order=desc";
var response = await _httpClient.GetStringAsync(url);
var json = JObject.Parse(response);
var bookIds = json["book_ids"]?.ToObject<List<int>>() ?? new List<int>();
foreach (var bookId in bookIds)
{
try
{
var bookUrl = $"{_baseUrl}/ajax/book/{bookId}";
var bookResponse = await _httpClient.GetStringAsync(bookUrl);
var bookJson = JObject.Parse(bookResponse);
var formats = bookJson["formats"]?.ToObject<List<string>>() ?? new List<string>();
var supportedFormat = formats.FirstOrDefault(f =>
f.Equals("EPUB", StringComparison.OrdinalIgnoreCase) ||
f.Equals("FB2", StringComparison.OrdinalIgnoreCase));
if (supportedFormat == null) continue;
var authors = bookJson["authors"]?.ToObject<List<string>>() ?? new List<string>();
var calibreBook = new CalibreBook
{
Id = bookId.ToString(),
Title = bookJson["title"]?.ToString() ?? "Unknown",
Author = string.Join(", ", authors),
Format = supportedFormat.ToLowerInvariant(),
CoverUrl = $"{_baseUrl}/get/cover/{bookId}",
DownloadUrl = $"{_baseUrl}/get/{supportedFormat}/{bookId}"
};
// Try to load cover
try
{
calibreBook.CoverImage = await _httpClient.GetByteArrayAsync(calibreBook.CoverUrl);
}
catch { }
books.Add(calibreBook);
}
catch { continue; }
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error fetching Calibre books: {ex.Message}");
}
return books;
}
public async Task<string> DownloadBookAsync(CalibreBook book, IProgress<double>? progress = null)
{
var booksDir = Path.Combine(FileSystem.AppDataDirectory, "Books");
Directory.CreateDirectory(booksDir);
var fileName = $"{Guid.NewGuid()}.{book.Format}";
var filePath = Path.Combine(booksDir, fileName);
using var response = await _httpClient.GetAsync(book.DownloadUrl, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
var totalBytes = response.Content.Headers.ContentLength ?? -1;
var bytesRead = 0L;
using var contentStream = await response.Content.ReadAsStreamAsync();
using var fileStream = File.Create(filePath);
var buffer = new byte[Constants.Network.DownloadBufferSize];
int read;
while ((read = await contentStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await fileStream.WriteAsync(buffer, 0, read);
bytesRead += read;
if (totalBytes > 0)
progress?.Report((double)bytesRead / totalBytes);
}
return filePath;
}
}