142 lines
5.0 KiB
C#
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;
|
|
}
|
|
} |