From 66e3531a7639da150dd1262a2b77de288e69dcf3 Mon Sep 17 00:00:00 2001 From: OnlyFart Date: Tue, 7 Jul 2026 18:03:42 +0300 Subject: [PATCH 01/29] Update README.md --- README.md | 487 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 339 insertions(+), 148 deletions(-) diff --git a/README.md b/README.md index 0d65318a..009329f3 100644 --- a/README.md +++ b/README.md @@ -1,181 +1,372 @@ -# Elib2Ebook -[![GitHub License](https://img.shields.io/github/license/OnlyFart/Elib2Ebook.svg?style=flat-square)](https://github.com/OnlyFart/Elib2Ebook/blob/master/LICENSE) -[![GitHub Stars](https://img.shields.io/github/stars/OnlyFart/Elib2Ebook.svg?style=flat-square)](https://github.com/OnlyFart/Elib2Ebook/stargazers) -[![GitHub forks](https://img.shields.io/github/forks/OnlyFart/Elib2Ebook.svg?style=flat-square)](https://github.com/OnlyFart/Elib2Ebook/network) -[![GitHub tag](https://img.shields.io/github/v/tag/OnlyFart/Elib2Ebook.svg?style=flat-square)](https://github.com/OnlyFart/Elib2Ebook/releases/latest) -![Docker Pulls](https://img.shields.io/docker/pulls/onlyfart/elib2ebookcli?style=flat-square&label=cli%20pulls) -![Docker Pulls](https://img.shields.io/docker/pulls/onlyfart/elib2ebookweb?style=flat-square&label=web%20pulls) -![GitHub downloads](https://img.shields.io/github/downloads/onlyfart/elib2ebook/total?style=flat-square) +# 📚 Elib2Ebook +[![License](https://img.shields.io/github/license/OnlyFart/Elib2Ebook.svg?style=flat-square)](LICENSE) +[![Stars](https://img.shields.io/github/stars/OnlyFart/Elib2Ebook.svg?style=flat-square)](https://github.com/OnlyFart/Elib2Ebook/stargazers) +[![Forks](https://img.shields.io/github/forks/OnlyFart/Elib2Ebook.svg?style=flat-square)](https://github.com/OnlyFart/Elib2Ebook/network) +[![Latest Tag](https://img.shields.io/github/v/tag/OnlyFart/Elib2Ebook.svg?style=flat-square)](https://github.com/OnlyFart/Elib2Ebook/releases/latest) +![Docker Pulls (CLI)](https://img.shields.io/docker/pulls/onlyfart/elib2ebookcli?style=flat-square&label=docker%20pulls%20(cli)) +![Docker Pulls (Web)](https://img.shields.io/docker/pulls/onlyfart/elib2ebookweb?style=flat-square&label=docker%20pulls%20(web)) +![GitHub Downloads (All)](https://img.shields.io/github/downloads/onlyfart/elib2ebook/total?style=flat-square) +**Elib2Ebook** — консольная утилита и веб-приложение для скачивания книг с популярных онлайн-библиотек и литературных сайтов. Поддерживает сохранение в форматах EPUB, FB2, CBZ, JSON, TXT. -Инструмент для сохранения любой доступной книги со следующих сайтов в форматах epub, fb2, cbz: -
-
-* http://samlib.ru/
-* https://acomics.ru/
-* https://author.today/
-* https://bigliba.com/
-* https://bllate.org/
-* https://bookinbook.ru/
-* https://bookhamster.ru/
-* https://bookinist.pw/
-* https://bookmate.ru/
-* https://booknet.com/
-* https://booknet.ua/
-* https://bookriver.ru/
-* https://bookstab.ru/
-* https://bookstime.ru/
-* https://bookuruk.com/
-* https://dark-novels.ru/
-* https://desu.me/
-* https://dreame.com/
-* https://erolate.com/
-* https://eznovels.com/
-* https://fanficus.com/
-* https://fb2.top/
-* https://ficbook.net/
-* https://fictionbook.ru/
-* https://hentailib.me/
-* https://hogwartsnet.ru/
-* https://hotnovelpub.com/
-* https://hub-book.com/
-* https://i-gram.ru/
-* https://ifreedom.su/
-* https://jaomix.ru/
-* https://ladylib.top/
-* https://lanovels.com/
-* https://libbox.ru/
-* https://librebook.me/
-* https://libst.ru/
-* https://lightnoveldaily.com/
-* https://litgorod.ru/
-* https://litlife.club/
-* https://litmarket.ru/
-* https://litmir.me/
-* https://litnet.com/
-* https://litres.ru/
-* https://litsovet.ru/
-* https://manga.ovh/
-* https://mangalib.me/
-* https://mangamammy.ru/
-* https://mir-knig.com/
-* https://mlate.ru/
-* https://mybook.ru/
-* https://neobook.org/
-* https://novelhall.com/
-* https://noveltranslate.com/
-* https://novelxo.com/
-* https://online-knigi.com.ua/
-* https://prodaman.ru/
-* https://ranobe-novels.ru/
-* https://ranobe.ovh/
-* https://ranobehub.org/
-* https://ranobelib.me/
-* https://ranobes.com/
-* https://readli.net/
-* https://readmanga.live/
-* https://remanga.org/
-* https://renovels.org/
-* https://romfant.ru/
-* https://royalroad.com/
-* https://ru.novelxo.com/
-* https://stroki.mts.ru/
-* https://tl.rulate.ru/
-* https://topliba.com/
-* https://twilightrussia.ru/
-* https://v2.slashlib.me/
-* https://wattpad.com/
-* https://wuxiaworld.ru/
-* https://younettranslate.com/
-* https://ранобэ.рф/
-
-
+--- -Последняя версия консольной утилиты доступна по [ссылке](https://github.com/OnlyFart/Elib2Ebook/releases/latest) +## Оглавление -Portable версии консольной утилиты запускаются без установленного NET Runtime. За настройку сборки Portalbe версий большая благодарность [@alfeg](https://github.com/alfeg) +- [Возможности](#возможности) +- [Форматы](#форматы) +- [Поддерживаемые сайты](#поддерживаемые-сайты) +- [Установка](#установка) + - [Консольная утилита](#консольная-утилита) + - [Docker (CLI)](#docker-cli) + - [Docker (Web)](#docker-web) + - [Сборка из исходников](#сборка-из-исходников) +- [Использование](#использование) + - [Обязательные параметры](#обязательные-параметры) + - [Авторизация](#авторизация) + - [Сеть и прокси](#сеть-и-прокси) + - [Сохранение файлов](#сохранение-файлов) + - [Главы](#главы) + - [Изображения](#изображения) + - [Дополнительные файлы](#дополнительные-файлы) + - [Временные файлы](#временные-файлы) + - [Шаблон имени файла](#шаблон-имени-файла) +- [Полный список параметров](#полный-список-параметров) +- [Скриншоты](#скриншоты) +- [Лицензия](#лицензия) -Для запуска не Portable версии консольной утилиты необходим установленный NET Runtime версии 9 или выше, который можно скачать с сайта Microsoft [здесь (на английском)](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) +--- -Web версия доступна для установки через [docker](https://www.docker.com) +## Возможности -## Пример вызова консольной утилиты -``` -Elib2EbookCli.exe -u https://author.today/work/212721 -f epub -``` +- **100+ сайтов** — ранобэ, фанфики, комиксы, манга, классическая литература +- **Несколько форматов одновременно** — сохраняйте книгу сразу в EPUB, FB2 и TXT +- **Гибкая загрузка глав** — по номеру (в том числе с конца) или по названию +- **Авторизация** — скачивание платных и закрытых книг +- **Обложка** — сохранение отдельным файлом +- **Режим «только текст»** — без загрузки изображений +- **Дополнительные файлы** — аудиоверсии, оригиналы книг +- **Прокси** — HTTP/HTTPS, SOCKS4, SOCKS5 +- **Flaresolverr** — обход Cloudflare +- **Шаблон имени** — настраиваемый формат выходного файла +- **Docker** — готовые образы для CLI и Web-версии (Blazor) -## Пример вызова консольной утилиты для генерации книги в нескольких форматах -``` -Elib2EbookCli.exe -u https://author.today/work/212721 -f epub,fb2 -``` +--- -## Пример вызова консольной утилиты для генерации книги с указанием начальной главы -``` -Elib2EbookCli.exe -u https://author.today/work/212721 -f epub,fb2 --start 3 -``` +## Форматы + +| Формат | Описание | +|:-------------|:------------------------------------| +| `epub` | EPUB (электронные книги) | +| `fb2` | FictionBook 2.0 | +| `cbz` | Comic Book Archive | +| `json` | JSON (полные данные) | +| `json_lite` | JSON (только текст и заголовки глав)| +| `txt` | Обычный текст | + +--- + +## Поддерживаемые сайты + +
+Нажмите, чтобы развернуть список (100+ сайтов) -## Пример вызова консольной утилиты для генерации книги с указанием конечной главы ``` -Elib2EbookCli.exe -u https://author.today/work/212721 -f epub,fb2 --end 10 +https://acomics.ru/ +https://author.today/ +https://bigliba.com/ +https://bllate.org/ +https://bookinbook.ru/ +https://bookhamster.ru/ +https://bookinist.pw/ +https://bookmate.ru/ +https://booknet.com/ +https://booknet.ua/ +https://bookriver.ru/ +https://bookstab.ru/ +https://bookstime.ru/ +https://bookuruk.com/ +https://dark-novels.ru/ +https://desu.me/ +https://dreame.com/ +https://erolate.com/ +https://eznovels.com/ +https://fanficus.com/ +https://fb2.top/ +https://ficbook.net/ +https://fictionbook.ru/ +https://hentailib.me/ +https://hogwartsnet.ru/ +https://hotnovelpub.com/ +https://hub-book.com/ +https://i-gram.ru/ +https://ifreedom.su/ +https://jaomix.ru/ +https://ladylib.top/ +https://lanovels.com/ +https://libbox.ru/ +https://librebook.me/ +https://libst.ru/ +https://lightnoveldaily.com/ +https://litgorod.ru/ +https://litlife.club/ +https://litmarket.ru/ +https://litmir.me/ +https://litnet.com/ +https://litres.ru/ +https://litsovet.ru/ +https://manga.ovh/ +https://mangalib.me/ +https://mangamammy.ru/ +https://mir-knig.com/ +https://mlate.ru/ +https://mybook.ru/ +https://neobook.org/ +https://novelhall.com/ +https://noveltranslate.com/ +https://novelxo.com/ +https://online-knigi.com.ua/ +https://prodaman.ru/ +https://ranobe-novels.ru/ +https://ranobe.ovh/ +https://ranobehub.org/ +https://ranobelib.me/ +https://ranobes.com/ +https://readli.net/ +https://readmanga.live/ +https://remanga.org/ +https://renovels.org/ +https://romfant.ru/ +https://royalroad.com/ +https://ru.novelxo.com/ +https://samlib.ru/ +https://stroki.mts.ru/ +https://tl.rulate.ru/ +https://topliba.com/ +https://twilightrussia.ru/ +https://v2.slashlib.me/ +https://wattpad.com/ +https://wuxiaworld.ru/ +https://younettranslate.com/ +https://ранобэ.рф/ ``` +
+ +--- + +## Установка + +### Консольная утилита + +**Вариант 1 — Portable (рекомендуется)** -## Пример вызова консольной утилиты для генерации книги с указанием начальной и конечной главы +Скачайте последнюю portable-версию со [страницы релизов](https://github.com/OnlyFart/Elib2Ebook/releases/latest). +Portable-сборки **не требуют установки .NET Runtime** (спасибо [@alfeg](https://github.com/alfeg) за настройку сборки). + +**Вариант 2 — Non-portable** + +Необходим [.NET Runtime 9.0+](https://dotnet.microsoft.com/en-us/download/dotnet/9.0). + +### Docker (CLI) + +```bash +docker run --rm \ + -v /путь/к/папке:/Save \ + onlyfart/elib2ebookcli \ + -u https://author.today/work/212721 \ + -f epub,fb2 \ + --save /Save ``` -Elib2EbookCli.exe -u https://author.today/work/212721 -f epub,fb2 --start 3 --end 10 + +### Docker (Web) + +```bash +docker run --rm \ + -p 8080:8080 \ + onlyfart/elib2ebookweb ``` -## Пример вызова консольной утилиты для генерации книги с указанием индекса начиная с конца (в книге будут 3 последних главы) +После запуска откройте в браузере [http://localhost:8080](http://localhost:8080). + +### Сборка из исходников + +```bash +git clone https://github.com/OnlyFart/Elib2Ebook.git +cd Elib2Ebook + +dotnet publish Elib2EbookCli/Elib2EbookCli.csproj \ + -c Release \ + -o ./publish/cli + +dotnet publish Elib2EbookWeb/Elib2EbookWeb.csproj \ + -c Release \ + -o ./publish/web ``` -Elib2EbookCli.exe -u https://author.today/work/212721 -f epub,fb2 --start -3 + +--- + +## Использование + +### Обязательные параметры + +```bash +# Одна книга, один формат +Elib2EbookCli --url https://author.today/work/212721 --format epub + +# Одна книга, несколько форматов сразу +Elib2EbookCli --url https://author.today/work/212721 --format epub,fb2,txt + +# Несколько книг одной командой (через запятую) +Elib2EbookCli --url https://author.today/work/212721,https://litnet.com/book/12345 --format epub ``` -## Пример вызова консольной утилиты для генерации книги с указанием индекса начиная с конца (в книге будет только предпоследняя глава) +### Авторизация + +```bash +# Базовая авторизация (логин + пароль) +Elib2EbookCli -u https://author.today/work/212721 -f epub --login vasya --password pupkin ``` -Elib2EbookCli.exe -u https://author.today/work/212721 -f epub,fb2 --start -2 --end -1 + +### Сеть и прокси + +```bash +# HTTP-прокси +Elib2EbookCli -u https://author.today/work/212721 -f epub --proxy http://proxy.example.com:8080 + +# SOCKS5-прокси +Elib2EbookCli -u https://author.today/work/212721 -f epub --proxy socks5://proxy.example.com:1080 + +# Flaresolverr (обход Cloudflare) +Elib2EbookCli -u https://author.today/work/212721 -f epub --flare http://localhost:8191 + +# Увеличенный таймаут (300 секунд) +Elib2EbookCli -u https://author.today/work/212721 -f epub --timeout 300 + +# Задержка между запросами (2 секунды) +Elib2EbookCli -u https://author.today/work/212721 -f epub --delay 2 ``` -## Пример вызова консольной утилиты c указанием логина и пароля для скачивания платных книг +### Сохранение файлов + +```bash +# Сохранить в указанную папку +Elib2EbookCli -u https://author.today/work/212721 -f epub --save "C:\Books" + +# Сохранить обложку отдельным файлом +Elib2EbookCli -u https://author.today/work/212721 -f epub --cover ``` -Elib2EbookCli.exe -u https://author.today/work/212721 -f epub,fb2 -l vasya -p pupkin + +### Главы + +```bash +# Диапазон глав (с 3-й по 10-ю) +Elib2EbookCli -u https://author.today/work/212721 -f epub --start 3 --end 10 + +# Только первые 5 глав (без указания end) +Elib2EbookCli -u https://author.today/work/212721 -f epub --end 5 + +# Только указанная глава +Elib2EbookCli -u https://author.today/work/212721 -f epub --start 7 --end 7 + +# Начиная с 5-й главы и до конца +Elib2EbookCli -u https://author.today/work/212721 -f epub --start 5 + +# Последние 3 главы (отрицательный индекс) +Elib2EbookCli -u https://author.today/work/212721 -f epub --start -3 + +# Только предпоследняя глава +Elib2EbookCli -u https://author.today/work/212721 -f epub --start -2 --end -1 + +# По названиям глав +Elib2EbookCli -u https://author.today/work/212721 -f epub --start-name "Глава 3" --end-name "Глава 10" + +# Без загрузки глав (только обложка и метаданные) +Elib2EbookCli -u https://author.today/work/212721 -f epub --no-chapters ``` -## Docker для консольной утилиты +### Изображения + +```bash +# Не загружать изображения (только текст) +Elib2EbookCli -u https://author.today/work/212721 -f epub --no-image ``` -docker run --rm -v <путь_к_папке_для_сохранения>:/Save onlyfart/elib2ebookcli -u https://author.today/work/212721 -f fb2,epub --save /Save + +### Дополнительные файлы + +```bash +# Сохранить все дополнительные файлы (аудио, оригиналы книг, изображения) +Elib2EbookCli -u https://author.today/work/212721 -f epub --additional + +# Сохранить только аудиоверсии +Elib2EbookCli -u https://author.today/work/212721 -f epub --additional --additional-types audio + +# Сохранить аудио и книги (без изображений) +Elib2EbookCli -u https://author.today/work/212721 -f epub --additional --additional-types audio,books ``` -## Docker для web версии +### Временные файлы + +```bash +# Указать папку для временных файлов +Elib2EbookCli -u https://author.today/work/212721 -f epub --temp "C:\Temp\Elib2Ebook" + +# Не удалять временные файлы после завершения (для отладки) +Elib2EbookCli -u https://author.today/work/212721 -f epub --save-temp ``` -docker run --rm -p 8080:8080 onlyfart/elib2ebookweb + +### Шаблон имени файла + +```bash +# Использовать шаблон по умолчанию: "Автор - Название.epub" +Elib2EbookCli -u https://author.today/work/212721 -f epub + +# Только название книги +Elib2EbookCli -u https://author.today/work/212721 -f epub --book-name-pattern "{Book.Title}" + +# Название + серия +Elib2EbookCli -u https://author.today/work/212721 -f epub --book-name-pattern "{Book.Title} [{Seria.Name}]" + +# Полный шаблон +Elib2EbookCli -u https://author.today/work/212721 -f epub --book-name-pattern "{Author.Name} - {Book.Title} [{Seria.Name}]" ``` +--- + +## Полный список параметров + +| Параметр | Алиас | Обязательный | По умолчанию | Описание | +|-------------------------|:-----:|:------------:|:------------------------------------:|---------------------------------------------------------------| +| `--url` | `-u` | ✅ | — | Ссылка на книгу (несколько — через запятую) | +| `--format` | `-f` | ✅ | — | Формат(ы): `epub`, `fb2`, `cbz`, `json`, `txt` | +| `--login` | `-l` | ❌ | — | Логин для авторизации | +| `--password` | `-p` | ❌ | — | Пароль для авторизации | +| `--proxy` | — | ❌ | — | Прокси: `(http\|socks4\|socks5)://host:port` | +| `--flare` | — | ❌ | — | Адрес Flaresolverr (обход Cloudflare) | +| `--save` | `-s` | ❌ | Текущая директория | Путь для сохранения книги | +| `--cover` | `-c` | ❌ | `false` | Сохранить обложку отдельным файлом | +| `--timeout` | `-t` | ❌ | `120` | Таймаут HTTP-запросов в секундах | +| `--delay` | `-d` | ❌ | `0` | Задержка между запросами в секундах | +| `--no-image` | — | ❌ | `false` | Не загружать изображения | +| `--start` | — | ❌ | — | Номер первой главы (отрицательное — с конца) | +| `--end` | — | ❌ | — | Номер последней главы (отрицательное — с конца) | +| `--start-name` | — | ❌ | — | Название первой главы | +| `--end-name` | — | ❌ | — | Название последней главы | +| `--no-chapters` | — | ❌ | `false` | Не загружать главы | +| `--temp` | — | ❌ | Системная временная папка | Директория для временных файлов | +| `--save-temp` | — | ❌ | `false` | Не удалять временные файлы после завершения | +| `--additional` | — | ❌ | `false` | Сохранить дополнительные файлы (аудио, оригиналы) | +| `--additional-types` | — | ❌ | Все типы | Типы доп. файлов: `books`, `audio`, `images` | +| `--book-name-pattern` | — | ❌ | `{Author.Name} - {Book.Title}` | Шаблон имени выходного файла | + +--- + ## Скриншоты -image - - - -## Полный список опций -| Команда | Описание | -|---------------------|-------------------------------------------------------------------------------------------| -| -u, --url | Обязательное. Ссылка на книгу | -| -f, --format | Обязательное. Формат для сохранения книги. Допустимые значения: epub, fb2, cbz, json, txt | -| -l, --login | Логин от системы | -| -p, --password | Пароль от системы | -| --proxy | Прокси в формате (http or socks4 or socks5)://host:port/ | -| -s, --save | Директория для сохранения книги | -| -c, --cover | Сохранить обложку книги в отдельный файл | -| -t, --timeout | (По-умолчанию: 120) Timeout для запросов в секундах | -| -d, --delay | (По-умолчанию: 0) Задержка между запросами в секундах | -| --no-image | Не загружать картинки | -| --temp | Директория для хранения временных файлов | -| --save-temp | Не удалять временную директорию | -| --start | Стартовый номер главы | -| --end | Конечный номер главы | -| --start-name | Стартовое имя главы | -| --end-name | Конечное имя главы | -| --no-chapters | Не загружать главы | -| --additional | Сохранить дополнительные файлы (оригинальные файлы книг, аудиоверсии и т.д.) | -| --additional-types | Типы дополнительных файлов. Допустимые значения: books, audio, images | -| --book-name-pattern | (По-умолчанию: {Author.Name} - {Book.Title}) Шаблон для имени файла. | -| --flare | Адрес flaresolverr | + +![Web-интерфейс](https://github.com/user-attachments/assets/05acc9fd-3cff-4137-af14-5826193f693f) + +--- + +## Лицензия + +Распространяется под лицензией [MIT](LICENSE). From ba2fc3b73a807b02fdd6bd94c2b9a828fe625b85 Mon Sep 17 00:00:00 2001 From: OnlyFart Date: Tue, 7 Jul 2026 18:08:29 +0300 Subject: [PATCH 02/29] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 009329f3..49938765 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # 📚 Elib2Ebook [![License](https://img.shields.io/github/license/OnlyFart/Elib2Ebook.svg?style=flat-square)](LICENSE) -[![Stars](https://img.shields.io/github/stars/OnlyFart/Elib2Ebook.svg?style=flat-square)](https://github.com/OnlyFart/Elib2Ebook/stargazers) [![Forks](https://img.shields.io/github/forks/OnlyFart/Elib2Ebook.svg?style=flat-square)](https://github.com/OnlyFart/Elib2Ebook/network) [![Latest Tag](https://img.shields.io/github/v/tag/OnlyFart/Elib2Ebook.svg?style=flat-square)](https://github.com/OnlyFart/Elib2Ebook/releases/latest) ![Docker Pulls (CLI)](https://img.shields.io/docker/pulls/onlyfart/elib2ebookcli?style=flat-square&label=docker%20pulls%20(cli)) From e82fa5dcce8fafbdde1e511aabb6a6a18f7e5b41 Mon Sep 17 00:00:00 2001 From: OnlyFart Date: Tue, 7 Jul 2026 22:42:19 +0300 Subject: [PATCH 03/29] refactoring --- .editorconfig | 65 ++++ .github/ISSUE_TEMPLATE/bug_report.md | 32 ++ .github/ISSUE_TEMPLATE/config.yml | 5 + .github/ISSUE_TEMPLATE/feature_request.md | 19 + .github/workflows/codeql.yml | 48 +++ .github/workflows/dotnet.yml | 107 ++++-- .gitignore | 82 ++--- Core/Core.csproj | 37 -- .../AuthorToday/AuthorTodayAuthResponse.cs | 11 - .../AuthorToday/AuthorTodayBookDetails.cs | 44 --- Core/Types/AuthorToday/AuthorTodayChapter.cs | 55 --- Core/Types/AuthorToday/AuthorTodayImage.cs | 8 - Core/Types/AuthorToday/AuthorTodayUser.cs | 8 - .../BookYandex/BooksYandexAudioBitrate.cs | 8 - .../BookYandex/BooksYandexAudioOffline.cs | 11 - .../Types/BookYandex/BooksYandexAudioTrack.cs | 8 - Core/Types/BookYandex/BooksYandexAuthor.cs | 11 - Core/Types/BookYandex/BooksYandexBook.cs | 50 --- .../BookYandex/BooksYandexComicContent.cs | 8 - .../BookYandex/BooksYandexComicMetadata.cs | 8 - Core/Types/BookYandex/BooksYandexComicPage.cs | 8 - Core/Types/BookYandex/BooksYandexCover.cs | 11 - Core/Types/BookYandex/BooksYandexEpisode.cs | 14 - Core/Types/BookYandex/BooksYandexEpisodes.cs | 8 - Core/Types/BookYandex/BooksYandexPlaylist.cs | 8 - Core/Types/BookYandex/BooksYandexResponse.cs | 14 - Core/Types/BookYandex/BooksYandexUri.cs | 11 - Core/Types/Bookriver/BookRiverApiResponse.cs | 8 - Core/Types/Bookriver/BookRiverAuthResponse.cs | 8 - Core/Types/Bookriver/BookRiverBook.cs | 8 - Core/Types/Bookriver/BookRiverChapter.cs | 11 - .../Bookriver/BookRiverChapterContent.cs | 8 - Core/Types/Bookriver/BookRiverCurrentBook.cs | 8 - Core/Types/Bookstab/BookstabApiResponse.cs | 11 - Core/Types/Bookstab/BookstabBook.cs | 20 -- Core/Types/Bookstab/BookstabChapter.cs | 14 - Core/Types/Bookstab/BookstabUser.cs | 11 - Core/Types/Bookstime/BookstimeAuthResponse.cs | 8 - Core/Types/Boosty/BoostyApiResponse.cs | 11 - Core/Types/Boosty/BoostyExtra.cs | 11 - Core/Types/Boosty/BoostyPost.cs | 15 - Core/Types/Common/IdChapter.cs | 3 - Core/Types/Common/UrlChapter.cs | 5 - Core/Types/DarkNovels/DarkNovelsAuthData.cs | 8 - Core/Types/DarkNovels/DarkNovelsAuthToken.cs | 8 - Core/Types/DarkNovels/DarkNovelsChapter.cs | 14 - Core/Types/DarkNovels/DarkNovelsData.cs | 14 - Core/Types/Dreame/DreameApiResponse.cs | 8 - Core/Types/Dreame/DreameCatalog.cs | 8 - Core/Types/Dreame/DreameChapter.cs | 14 - Core/Types/Dreame/DreameNovel.cs | 26 -- Core/Types/Dreame/DreamePager.cs | 9 - Core/Types/FanFicus/FanFicusApiResponse.cs | 8 - Core/Types/FanFicus/FanFicusBook.cs | 17 - Core/Types/FanFicus/FanFicusChapter.cs | 8 - Core/Types/FanFicus/FanFicusCreator.cs | 11 - Core/Types/FanFicus/FanFicusImage.cs | 8 - Core/Types/FanFicus/FanFicusPart.cs | 11 - Core/Types/FanFicus/FanFicusUser.cs | 8 - Core/Types/Freedlit/FreedlitApiResponse.cs | 8 - Core/Types/Freedlit/FreedlitApp.cs | 8 - Core/Types/Freedlit/FreedlitAuthError.cs | 11 - Core/Types/Freedlit/FreedlitAuthor.cs | 14 - Core/Types/Freedlit/FreedlitBook.cs | 23 -- Core/Types/Freedlit/FreedlitChapter.cs | 14 - Core/Types/Freedlit/FreedlitItemsContent.cs | 9 - Core/Types/Freedlit/FreedlitProps.cs | 11 - .../Types/Freedlit/FreedlitSuccessResponse.cs | 9 - .../HotNovelPub/HotNovelPubApiResponse.cs | 8 - .../Types/HotNovelPub/HotNovelPubAuthorize.cs | 14 - Core/Types/HotNovelPub/HotNovelPubBook.cs | 17 - .../HotNovelPub/HotNovelPubBookResponse.cs | 12 - Core/Types/HotNovelPub/HotNovelPubChapter.cs | 14 - Core/Types/Librebook/LibrebookAuthResponse.cs | 11 - Core/Types/Litexit/LitexitChapter.cs | 14 - Core/Types/Litexit/LitexitUser.cs | 8 - Core/Types/Litgorod/LitgorodAuthResponse.cs | 8 - Core/Types/Litgorod/LitgorodBookPart.cs | 8 - Core/Types/Litgorod/LitgorodChapter.cs | 8 - Core/Types/Litmarket/AuthResponse.cs | 8 - Core/Types/Litmarket/Block.cs | 11 - Core/Types/Litmarket/Chunk.cs | 8 - Core/Types/Litmarket/LBook.cs | 8 - Core/Types/Litmarket/Mod.cs | 21 -- Core/Types/Litmarket/Response.cs | 11 - Core/Types/Litnet/LitnetAuthResponse.cs | 11 - Core/Types/Litnet/LitnetBookResponse.cs | 38 -- Core/Types/Litnet/LitnetChapterResponse.cs | 11 - Core/Types/Litnet/LitnetContentsResponse.cs | 11 - Core/Types/Litres/LitresPayload.cs | 47 --- Core/Types/Litres/Requests/LitresAuthData.cs | 11 - .../Litres/Requests/LitresBrowseArtsData.cs | 14 - .../Litres/Requests/LitresRequestBase.cs | 14 - Core/Types/Litres/Response/LitresArt.cs | 29 -- .../Litres/Response/LitresAuthResponseData.cs | 14 - Core/Types/Litres/Response/LitresFile.cs | 11 - Core/Types/Litres/Response/LitresFiles.cs | 8 - Core/Types/Litres/Response/LitresMe.cs | 8 - .../Response/LitresPartnerSubscriptions.cs | 8 - Core/Types/Litres/Response/LitresPerson.cs | 17 - Core/Types/Litres/Response/LitresResponse.cs | 8 - Core/Types/Litres/Response/LitresSeria.cs | 14 - .../Litres/Response/LitresStaticResponse.cs | 8 - .../Litres/Response/LitresSubscription.cs | 14 - Core/Types/Litsovet/LitsovetLoginResponse.cs | 8 - Core/Types/MangaLib/MangaLibPg.cs | 11 - Core/Types/MyBook/MyBookAuth.cs | 11 - Core/Types/MyBook/MyBookAuthor.cs | 11 - Core/Types/MyBook/MyBookBook.cs | 36 -- Core/Types/MyBook/MyBookFile.cs | 14 - Core/Types/MyBook/MyBookMapFile.cs | 8 - Core/Types/Neobook/NeobookAttachment.cs | 9 - Core/Types/Neobook/NeobookAuth.cs | 11 - Core/Types/Neobook/NeobookBook.cs | 11 - Core/Types/Neobook/NeobookChapter.cs | 8 - Core/Types/Neobook/NeobookChapterData.cs | 8 - Core/Types/Neobook/NeobookLogin.cs | 11 - Core/Types/Neobook/NeobookPostData.cs | 20 -- Core/Types/Neobook/NeobookTocChapter.cs | 11 - Core/Types/Neobook/NeobookUser.cs | 14 - Core/Types/Ranobe/RanobeBook.cs | 21 -- Core/Types/Ranobe/RanobeChapter.cs | 8 - Core/Types/Ranobe/RanobeChapterContent.cs | 8 - Core/Types/Ranobe/RanobeChapterShort.cs | 11 - Core/Types/Ranobe/RanobeImage.cs | 11 - Core/Types/RanobeHub/RanobeHubApiResponse.cs | 8 - Core/Types/RanobeHub/RanobeHubChapter.cs | 11 - Core/Types/RanobeHub/RanobeHubVolume.cs | 8 - .../Types/RanobeNovels/RanobeNovelsChapter.cs | 11 - Core/Types/RanobeOvh/RanobeOvhBranch.cs | 14 - Core/Types/RanobeOvh/RanobeOvhChapterFull.cs | 64 ---- Core/Types/RanobeOvh/RanobeOvhManga.cs | 14 - Core/Types/RanobeOvh/RanobeOvhMangaName.cs | 8 - Core/Types/RanobeOvh/RanobeOvhPage.cs | 11 - Core/Types/RanobeOvh/RanoveOvhTranslator.cs | 11 - Core/Types/Ranobes/RanobesCookie.cs | 8 - Core/Types/Renovels/RenovelsApiResponse.cs | 11 - Core/Types/Renovels/RenovelsAuthResponse.cs | 8 - Core/Types/Renovels/RenovelsBranch.cs | 11 - Core/Types/Renovels/RenovelsChapter.cs | 41 --- Core/Types/Renovels/RenovelsContent.cs | 24 -- Core/Types/Renovels/RenovelsPage.cs | 8 - Core/Types/Renovels/RenovelsPublisher.cs | 11 - Core/Types/Renovels/RenovelsTocResponse.cs | 8 - Core/Types/Rulate/RulateAuthResponse.cs | 8 - Core/Types/Rulate/RulateChapter.cs | 11 - Core/Types/SocialLib/LibSocialToken.cs | 8 - Core/Types/SocialLib/RanobeLibBookDetail.cs | 49 --- Core/Types/SocialLib/SocialLibBookChapters.cs | 90 ----- Core/Types/SocialLib/SocialLibChapter.cs | 22 -- Core/Types/SocialLib/SocialLibChapterOld.cs | 21 -- Core/Types/SocialLib/SocialLibChapters.cs | 9 - Core/Types/SocialLib/User.cs | 8 - Core/Types/SocialLib/WindowData.cs | 11 - .../StrokiMts/StrokiMtsApiMultiResponse.cs | 9 - Core/Types/StrokiMts/StrokiMtsApiResponse.cs | 8 - Core/Types/StrokiMts/StrokiMtsAuthor.cs | 11 - Core/Types/StrokiMts/StrokiMtsBookItem.cs | 24 -- Core/Types/StrokiMts/StrokiMtsContentInfo.cs | 8 - Core/Types/StrokiMts/StrokiMtsFile.cs | 8 - Core/Types/StrokiMts/StrokiMtsFileUrl.cs | 8 - Core/Types/StrokiMts/StrokiMtsFiles.cs | 11 - Core/Types/StrokiMts/StrokiMtsMultiItem.cs | 17 - Core/Types/Wattpad/WattpadInfo.cs | 20 -- Core/Types/Wattpad/WattpadMeta.cs | 5 - Core/Types/Wattpad/WattpadUser.cs | 8 - Core/Types/WuxiaWorld/WuxiaWorldToc.cs | 8 - Directory.Build.props | 9 + Directory.Packages.props | 24 ++ Elib2Ebook.sln | 67 +++- Elib2EbookCli/ConsoleLogger.cs | 15 - Elib2EbookCli/Elib2EbookCli.csproj | 24 -- Elib2EbookWeb/Components/App.razor | 40 --- Elib2EbookWeb/Components/PWA.razor | 22 -- Elib2EbookWeb/Components/ShortBook.razor | 63 ---- Elib2EbookWeb/Elib2EbookWeb.csproj | 27 -- Elib2EbookWeb/Misc/SbWriter.cs | 17 - Elib2EbookWeb/Properties/launchSettings.json | 38 -- README.md | 5 +- .../Core}/Configs/BookGetterConfig.cs | 107 +++--- {Core => src/Core}/Configs/Options.cs | 76 ++-- src/Core/Core.csproj | 28 ++ .../Extensions/FileProviderExtensions.cs | 20 +- .../Extensions/HtmlDocumentExtensions.cs | 50 +-- .../Core}/Extensions/HttpClientExtensions.cs | 161 +++++---- .../Core}/Extensions/ImageFormatExtensions.cs | 14 +- .../Core}/Extensions/StreamExtension.cs | 11 +- .../Extensions/StringBuilderExtensions.cs | 10 +- .../Core}/Extensions/StringExtensions.cs | 80 +++-- {Core => src/Core}/Extensions/UriExtension.cs | 64 ++-- .../Core}/External/EpubSharp.deps.json | 0 {Core => src/Core}/External/EpubSharp.dll | Bin {Core => src/Core}/External/EpubSharp.pdb | Bin {Core => src/Core}/Logic/BookNameBuilder.cs | 14 +- .../Logic/Builders/AdditionaFileBuilder.cs | 48 ++- .../Core}/Logic/Builders/BuilderBase.cs | 51 +-- .../Core}/Logic/Builders/CbzBuilder.cs | 28 +- .../Core}/Logic/Builders/EpubBuilder.cs | 85 +++-- .../Core}/Logic/Builders/Fb2Builder.cs | 315 +++++++++++------ .../Core}/Logic/Builders/JsonBuilder.cs | 19 +- .../Core}/Logic/Builders/JsonLiteBuilder.cs | 53 +-- .../Core}/Logic/Builders/TxtBuilder.cs | 40 ++- {Core => src/Core}/Logic/FileProvider.cs | 16 +- .../Core}/Logic/Getters/AcomicsGetter.cs | 51 +-- .../Core}/Logic/Getters/AuthorTodayGetter.cs | 156 +++++--- .../Core}/Logic/Getters/BookInBookGetter.cs | 94 +++-- .../Core}/Logic/Getters/BookRiverGetter.cs | 125 ++++--- .../Core}/Logic/Getters/BookinistGetter.cs | 50 +-- .../BooksYandex/BooksYandexBooksGetter.cs | 133 ++++--- .../BooksYandex/BooksYandexComicsGetter.cs | 29 +- .../BooksYandex/BooksYandexGetterBase.cs | 83 +++-- .../Core}/Logic/Getters/BookstabGetter.cs | 67 ++-- .../Core}/Logic/Getters/BookstimeGetter.cs | 107 +++--- .../Core}/Logic/Getters/BookurukGetter.cs | 103 +++--- .../Core}/Logic/Getters/BoostyGetter.cs | 81 +++-- .../Core}/Logic/Getters/BoovelGetter.cs | 58 +-- .../Core}/Logic/Getters/DarkNovelsGetter.cs | 122 ++++--- .../Core}/Logic/Getters/DesuGetter.cs | 72 ++-- .../Core}/Logic/Getters/DreameGetter.cs | 73 ++-- .../Core}/Logic/Getters/FanFicusGetter.cs | 83 +++-- .../Logic/Getters/Fb2Top/Fb2TopGetter.cs | 8 +- .../Logic/Getters/Fb2Top/Fb2TopGetterBase.cs | 71 ++-- .../Logic/Getters/Fb2Top/LadyLibGetter.cs | 8 +- .../Core}/Logic/Getters/FicbookGetter.cs | 86 +++-- .../Core}/Logic/Getters/FictionBookGetter.cs | 102 +++--- .../Core}/Logic/Getters/FreedlitGetter.cs | 103 ++++-- .../Getters/Freedom/BookHamsterGetter.cs | 6 +- .../Logic/Getters/Freedom/FreedomGetter.cs | 6 +- .../Getters/Freedom/FreedomGetterBase.cs | 69 ++-- .../Core}/Logic/Getters/GetterBase.cs | 315 ++++++++++------- .../Core}/Logic/Getters/HogwartsNetGetter.cs | 107 +++--- .../Getters/HotNovelPub/EzNovelsGetter.cs | 8 +- .../Getters/HotNovelPub/HotNovelPubGetter.cs | 8 +- .../HotNovelPub/HotNovelPubGetterBase.cs | 66 ++-- .../Getters/HotNovelPub/LaNovelsGetter.cs | 8 +- .../HotNovelPub/LightNovelDailyGetter.cs | 8 +- .../Core}/Logic/Getters/HubBookGetter.cs | 47 ++- .../Core}/Logic/Getters/IGramGetter.cs | 96 ++--- .../Core}/Logic/Getters/JaomixGetter.cs | 70 ++-- .../Getters/LibSocial/HentaiLibGetter.cs | 6 +- .../Logic/Getters/LibSocial/MangaLibGetter.cs | 4 +- .../Getters/LibSocial/MangaLibGetterBase.cs | 13 +- .../Getters/LibSocial/MangaLibOrgGetter.cs | 4 +- .../LibSocial/NewLibSocialGetterBase.cs | 207 +++++++---- .../Getters/LibSocial/RanobeLibGetter.cs | 120 ++++--- .../Logic/Getters/LibSocial/SlashLibGetter.cs | 8 +- .../Core}/Logic/Getters/LibboxGetter.cs | 100 +++--- .../Core}/Logic/Getters/LibkingGetter.cs | 98 ++++-- .../Core}/Logic/Getters/LibrebookGetter.cs | 110 +++--- .../Core}/Logic/Getters/LibstGetter.cs | 111 +++--- .../Core}/Logic/Getters/LitLifeGetter.cs | 122 ++++--- .../Core}/Logic/Getters/LitgorodGetter.cs | 124 ++++--- .../Core}/Logic/Getters/LitmarketGetter.cs | 151 ++++---- .../Core}/Logic/Getters/LitmirGetter.cs | 131 ++++--- .../Logic/Getters/Litnet/BooknetComGetter.cs | 8 +- .../Logic/Getters/Litnet/BooknetUaGetter.cs | 8 +- .../Logic/Getters/Litnet/LitnetGetter.cs | 8 +- .../Logic/Getters/Litnet/LitnetGetterBase.cs | 176 ++++++---- .../Core}/Logic/Getters/LitresGetter.cs | 332 +++++++++++------- .../Core}/Logic/Getters/LitsovetGetter.cs | 102 ++++-- .../Core}/Logic/Getters/MangaMammyGetter.cs | 59 ++-- .../Core}/Logic/Getters/MirKnigGetter.cs | 66 ++-- .../Core}/Logic/Getters/MyBookGetter.cs | 144 ++++---- .../Core}/Logic/Getters/NeobookGetter.cs | 117 +++--- .../Logic/Getters/NovelTranslateGetter.cs | 71 ++-- .../Core}/Logic/Getters/NovelhallGetter.cs | 53 +-- .../Logic/Getters/Novelxo/NovelxoGetter.cs | 8 +- .../Getters/Novelxo/NovelxoGetterBase.cs | 129 ++++--- .../Logic/Getters/Novelxo/NovelxoRuGetter.cs | 8 +- .../Core}/Logic/Getters/OnlineKnigiGetter.cs | 50 +-- .../Core}/Logic/Getters/ProdamanGetter.cs | 166 +++++---- .../Core}/Logic/Getters/RanobeGetter.cs | 44 ++- .../Core}/Logic/Getters/RanobeHubGetter.cs | 70 ++-- .../Core}/Logic/Getters/RanobeNovelsGetter.cs | 74 ++-- .../Logic/Getters/RanobeOvh/MangaOvhGetter.cs | 15 +- .../Logic/Getters/RanobeOvh/NovelOvhGetter.cs | 118 ++++--- .../Getters/RanobeOvh/RanobeOvhGetterBase.cs | 59 ++-- .../Core}/Logic/Getters/RanobesComGetter.cs | 167 ++++++--- .../Core}/Logic/Getters/ReadMangaGetter.cs | 70 ++-- .../Core}/Logic/Getters/ReadliGetter.cs | 135 ++++--- .../Logic/Getters/Renovels/RemangaGetter.cs | 27 +- .../Logic/Getters/Renovels/RenovelsGetter.cs | 15 +- .../Getters/Renovels/RenovelsGetterBase.cs | 98 +++--- .../Core}/Logic/Getters/RomfantGetter.cs | 86 +++-- .../Core}/Logic/Getters/RoyalRoadGetter.cs | 58 +-- .../Logic/Getters/Rulate/BllateGetter.cs | 6 +- .../Logic/Getters/Rulate/ErolateGetter.cs | 8 +- .../Core}/Logic/Getters/Rulate/MlateGetter.cs | 8 +- .../Logic/Getters/Rulate/RulateGetter.cs | 8 +- .../Logic/Getters/Rulate/RulateGetterBase.cs | 123 ++++--- .../Core}/Logic/Getters/SamlibGetter.cs | 121 ++++--- .../Core}/Logic/Getters/StrokiMtsGetter.cs | 197 ++++++----- .../Logic/Getters/TopLiba/BiglibaGetter.cs | 39 +- .../Logic/Getters/TopLiba/TopLibaGetter.cs | 38 +- .../Getters/TopLiba/TopLibaGetterBase.cs | 98 +++--- .../Logic/Getters/TwilightRussiaGetter.cs | 72 ++-- .../Core}/Logic/Getters/WattpadGetter.cs | 51 +-- .../Core}/Logic/Getters/WuxiaWorldGetter.cs | 81 +++-- .../Logic/Getters/YounettranslateGetter.cs | 66 ++-- {Core => src/Core}/Misc/AdditionalTypeEnum.cs | 7 +- {Core => src/Core}/Misc/BuilderProvider.cs | 12 +- {Core => src/Core}/Misc/GetterProvider.cs | 12 +- .../Core}/Misc/TempFolder/TempFolder.cs | 22 +- .../Misc/TempFolder/TempFolderFactory.cs | 17 +- .../Core}/Patterns/ChapterPattern.xhtml | 12 +- {Core => src/Core}/Patterns/Roboto-Italic.ttf | Bin .../Core}/Patterns/Roboto-Regular.ttf | Bin {Core => src/Core}/Patterns/stylesheet.css | 18 +- .../AuthorToday/AuthorTodayAuthResponse.cs | 10 + .../AuthorToday/AuthorTodayBookDetails.cs | 32 ++ .../Types/AuthorToday/AuthorTodayChapter.cs | 46 +++ .../Types/AuthorToday/AuthorTodayImage.cs | 8 + src/Core/Types/AuthorToday/AuthorTodayUser.cs | 8 + .../Types/Book/AdditionalFileCollection.cs | 33 +- {Core => src/Core}/Types/Book/Author.cs | 14 +- {Core => src/Core}/Types/Book/Book.cs | 21 +- {Core => src/Core}/Types/Book/Chapter.cs | 10 +- {Core => src/Core}/Types/Book/Seria.cs | 13 +- .../BookYandex/BooksYandexAudioBitrate.cs | 8 + .../BookYandex/BooksYandexAudioOffline.cs | 10 + .../Types/BookYandex/BooksYandexAudioTrack.cs | 8 + .../Types/BookYandex/BooksYandexAuthor.cs | 10 + src/Core/Types/BookYandex/BooksYandexBook.cs | 44 +++ .../BookYandex/BooksYandexComicContent.cs | 8 + .../BookYandex/BooksYandexComicMetadata.cs | 8 + .../Types/BookYandex/BooksYandexComicPage.cs | 8 + src/Core/Types/BookYandex/BooksYandexCover.cs | 10 + .../Types/BookYandex/BooksYandexEpisode.cs | 12 + .../Types/BookYandex/BooksYandexEpisodes.cs | 8 + .../Types/BookYandex/BooksYandexPlaylist.cs | 8 + .../Types/BookYandex/BooksYandexResponse.cs | 12 + src/Core/Types/BookYandex/BooksYandexUri.cs | 10 + .../Types/Bookriver/BookRiverApiResponse.cs | 8 + .../Types/Bookriver/BookRiverAuthResponse.cs | 8 + src/Core/Types/Bookriver/BookRiverBook.cs | 8 + src/Core/Types/Bookriver/BookRiverChapter.cs | 10 + .../Bookriver/BookRiverChapterContent.cs | 8 + .../Types/Bookriver/BookRiverCurrentBook.cs | 8 + .../Types/Bookstab/BookstabApiResponse.cs | 10 + src/Core/Types/Bookstab/BookstabBook.cs | 16 + src/Core/Types/Bookstab/BookstabChapter.cs | 12 + src/Core/Types/Bookstab/BookstabUser.cs | 10 + .../Types/Bookstime/BookstimeAuthResponse.cs | 8 + src/Core/Types/Boosty/BoostyApiResponse.cs | 10 + src/Core/Types/Boosty/BoostyExtra.cs | 10 + src/Core/Types/Boosty/BoostyPost.cs | 12 + src/Core/Types/Common/IdChapter.cs | 3 + {Core => src/Core}/Types/Common/TempFile.cs | 58 +-- src/Core/Types/Common/UrlChapter.cs | 3 + .../Types/DarkNovels/DarkNovelsAuthData.cs | 8 + .../Types/DarkNovels/DarkNovelsAuthToken.cs | 8 + .../Types/DarkNovels/DarkNovelsChapter.cs | 12 + src/Core/Types/DarkNovels/DarkNovelsData.cs | 12 + src/Core/Types/Dreame/DreameApiResponse.cs | 8 + src/Core/Types/Dreame/DreameCatalog.cs | 8 + src/Core/Types/Dreame/DreameChapter.cs | 12 + src/Core/Types/Dreame/DreameNovel.cs | 20 ++ src/Core/Types/Dreame/DreamePager.cs | 8 + .../Types/FanFicus/FanFicusApiResponse.cs | 8 + src/Core/Types/FanFicus/FanFicusBook.cs | 14 + src/Core/Types/FanFicus/FanFicusChapter.cs | 8 + src/Core/Types/FanFicus/FanFicusCreator.cs | 10 + src/Core/Types/FanFicus/FanFicusImage.cs | 8 + src/Core/Types/FanFicus/FanFicusPart.cs | 10 + src/Core/Types/FanFicus/FanFicusUser.cs | 8 + .../Types/Freedlit/FreedlitApiResponse.cs | 8 + src/Core/Types/Freedlit/FreedlitApp.cs | 8 + src/Core/Types/Freedlit/FreedlitAuthError.cs | 10 + src/Core/Types/Freedlit/FreedlitAuthor.cs | 12 + src/Core/Types/Freedlit/FreedlitBook.cs | 18 + src/Core/Types/Freedlit/FreedlitChapter.cs | 12 + .../Types/Freedlit/FreedlitItemsContent.cs | 8 + src/Core/Types/Freedlit/FreedlitProps.cs | 10 + .../Types/Freedlit/FreedlitSuccessResponse.cs | 8 + .../HotNovelPub/HotNovelPubApiResponse.cs | 8 + .../Types/HotNovelPub/HotNovelPubAuthorize.cs | 12 + src/Core/Types/HotNovelPub/HotNovelPubBook.cs | 14 + .../HotNovelPub/HotNovelPubBookResponse.cs | 10 + .../Types/HotNovelPub/HotNovelPubChapter.cs | 12 + .../Types/Librebook/LibrebookAuthResponse.cs | 10 + src/Core/Types/Litexit/LitexitChapter.cs | 12 + src/Core/Types/Litexit/LitexitUser.cs | 8 + .../Types/Litgorod/LitgorodAuthResponse.cs | 8 + src/Core/Types/Litgorod/LitgorodBookPart.cs | 8 + src/Core/Types/Litgorod/LitgorodChapter.cs | 8 + src/Core/Types/Litmarket/AuthResponse.cs | 8 + src/Core/Types/Litmarket/Block.cs | 10 + src/Core/Types/Litmarket/Chunk.cs | 8 + src/Core/Types/Litmarket/LBook.cs | 8 + src/Core/Types/Litmarket/Mod.cs | 17 + src/Core/Types/Litmarket/Response.cs | 10 + src/Core/Types/Litnet/LitnetAuthResponse.cs | 10 + src/Core/Types/Litnet/LitnetBookResponse.cs | 28 ++ .../Types/Litnet/LitnetChapterResponse.cs | 10 + .../Types/Litnet/LitnetContentsResponse.cs | 10 + {Core => src/Core}/Types/Litres/LitresBook.cs | 8 +- src/Core/Types/Litres/LitresPayload.cs | 40 +++ .../Core}/Types/Litres/LitresTarget.cs | 7 +- .../Types/Litres/Requests/LitresAuthData.cs | 10 + .../Litres/Requests/LitresAuthRequest.cs | 10 +- .../Litres/Requests/LitresBrowseArtsData.cs | 12 + .../Requests/LitresBrowseArtsRequest.cs | 10 +- .../Litres/Requests/LitresRequestBase.cs | 12 + src/Core/Types/Litres/Response/LitresArt.cs | 22 ++ .../Litres/Response/LitresArtTypeEnum.cs | 5 +- .../Litres/Response/LitresAuthResponseData.cs | 12 + src/Core/Types/Litres/Response/LitresFile.cs | 10 + src/Core/Types/Litres/Response/LitresFiles.cs | 8 + src/Core/Types/Litres/Response/LitresMe.cs | 8 + .../Response/LitresPartnerSubscriptions.cs | 8 + .../Types/Litres/Response/LitresPerson.cs | 14 + .../Types/Litres/Response/LitresResponse.cs | 8 + src/Core/Types/Litres/Response/LitresSeria.cs | 12 + .../Litres/Response/LitresStaticResponse.cs | 8 + .../Litres/Response/LitresSubscription.cs | 12 + .../Types/Litsovet/LitsovetLoginResponse.cs | 8 + src/Core/Types/MangaLib/MangaLibPg.cs | 10 + src/Core/Types/MyBook/MyBookAuth.cs | 10 + src/Core/Types/MyBook/MyBookAuthor.cs | 10 + src/Core/Types/MyBook/MyBookBook.cs | 27 ++ src/Core/Types/MyBook/MyBookFile.cs | 12 + src/Core/Types/MyBook/MyBookMapFile.cs | 8 + src/Core/Types/Neobook/NeobookAttachment.cs | 8 + src/Core/Types/Neobook/NeobookAuth.cs | 10 + src/Core/Types/Neobook/NeobookBook.cs | 10 + src/Core/Types/Neobook/NeobookChapter.cs | 8 + src/Core/Types/Neobook/NeobookChapterData.cs | 8 + src/Core/Types/Neobook/NeobookLogin.cs | 10 + src/Core/Types/Neobook/NeobookPostData.cs | 16 + src/Core/Types/Neobook/NeobookTocChapter.cs | 10 + src/Core/Types/Neobook/NeobookUser.cs | 12 + src/Core/Types/Ranobe/RanobeBook.cs | 16 + src/Core/Types/Ranobe/RanobeChapter.cs | 8 + src/Core/Types/Ranobe/RanobeChapterContent.cs | 8 + src/Core/Types/Ranobe/RanobeChapterShort.cs | 10 + src/Core/Types/Ranobe/RanobeImage.cs | 10 + .../Types/RanobeHub/RanobeHubApiResponse.cs | 8 + src/Core/Types/RanobeHub/RanobeHubChapter.cs | 10 + src/Core/Types/RanobeHub/RanobeHubVolume.cs | 8 + .../Types/RanobeNovels/RanobeNovelsChapter.cs | 10 + src/Core/Types/RanobeOvh/RanobeOvhBranch.cs | 12 + .../Types/RanobeOvh/RanobeOvhChapterFull.cs | 56 +++ src/Core/Types/RanobeOvh/RanobeOvhManga.cs | 12 + .../Types/RanobeOvh/RanobeOvhMangaName.cs | 8 + src/Core/Types/RanobeOvh/RanobeOvhPage.cs | 10 + .../Types/RanobeOvh/RanoveOvhTranslator.cs | 10 + src/Core/Types/Ranobes/RanobesCookie.cs | 8 + .../Types/Renovels/RenovelsApiResponse.cs | 10 + .../Types/Renovels/RenovelsAuthResponse.cs | 8 + src/Core/Types/Renovels/RenovelsBranch.cs | 10 + src/Core/Types/Renovels/RenovelsChapter.cs | 37 ++ src/Core/Types/Renovels/RenovelsContent.cs | 18 + src/Core/Types/Renovels/RenovelsPage.cs | 8 + src/Core/Types/Renovels/RenovelsPublisher.cs | 10 + .../Types/Renovels/RenovelsTocResponse.cs | 8 + src/Core/Types/Rulate/RulateAuthResponse.cs | 8 + src/Core/Types/Rulate/RulateChapter.cs | 10 + src/Core/Types/SocialLib/LibSocialToken.cs | 8 + .../Types/SocialLib/RanobeLibBookDetail.cs | 42 +++ .../Types/SocialLib/SocialLibBookChapters.cs | 82 +++++ src/Core/Types/SocialLib/SocialLibChapter.cs | 18 + .../Types/SocialLib/SocialLibChapterOld.cs | 19 + src/Core/Types/SocialLib/SocialLibChapters.cs | 8 + src/Core/Types/SocialLib/User.cs | 8 + src/Core/Types/SocialLib/WindowData.cs | 10 + .../StrokiMts/StrokiMtsApiMultiResponse.cs | 8 + .../Types/StrokiMts/StrokiMtsApiResponse.cs | 8 + src/Core/Types/StrokiMts/StrokiMtsAuthor.cs | 10 + src/Core/Types/StrokiMts/StrokiMtsBookItem.cs | 18 + .../Types/StrokiMts/StrokiMtsContentInfo.cs | 8 + src/Core/Types/StrokiMts/StrokiMtsFile.cs | 8 + src/Core/Types/StrokiMts/StrokiMtsFileUrl.cs | 8 + src/Core/Types/StrokiMts/StrokiMtsFiles.cs | 10 + .../Types/StrokiMts/StrokiMtsMultiItem.cs | 14 + src/Core/Types/Wattpad/WattpadInfo.cs | 16 + src/Core/Types/Wattpad/WattpadMeta.cs | 6 + .../Core}/Types/Wattpad/WattpadPart.cs | 16 +- src/Core/Types/Wattpad/WattpadUser.cs | 8 + src/Core/Types/WuxiaWorld/WuxiaWorldToc.cs | 8 + src/Elib2EbookCli/ConsoleLogger.cs | 19 + .../Elib2EbookCli}/Dockerfile | 4 +- src/Elib2EbookCli/Elib2EbookCli.csproj | 21 ++ .../Elib2EbookCli}/Program.cs | 77 ++-- src/Elib2EbookWeb/Components/App.razor | 40 +++ .../Elib2EbookWeb}/Components/BookForm.razor | 120 ++++--- .../Components/Layout/MainLayout.razor | 17 +- .../Components/Layout/MainLayout.razor.css | 36 +- src/Elib2EbookWeb/Components/PWA.razor | 32 ++ .../Components/Pages/Error.razor | 20 +- .../Components/Pages/Home.razor | 43 ++- .../Elib2EbookWeb}/Components/Routes.razor | 0 src/Elib2EbookWeb/Components/ShortBook.razor | 83 +++++ .../Elib2EbookWeb}/Components/_Imports.razor | 0 .../Elib2EbookWeb}/Dockerfile | 4 +- src/Elib2EbookWeb/Elib2EbookWeb.csproj | 19 + src/Elib2EbookWeb/Misc/SbWriter.cs | 22 ++ .../Elib2EbookWeb}/Program.cs | 8 +- .../Properties/launchSettings.json | 38 ++ .../appsettings.Development.json | 0 .../Elib2EbookWeb}/appsettings.json | 0 .../Elib2EbookWeb}/wwwroot/app.css | 10 +- .../wwwroot/bootstrap/bootstrap.min.css | 0 .../wwwroot/bootstrap/bootstrap.min.css.map | 0 .../Elib2EbookWeb}/wwwroot/favicon.png | Bin .../wwwroot/icons/icon128x128.png | Bin .../wwwroot/icons/icon256x256.png | Bin .../wwwroot/icons/icon32x32.png | Bin .../wwwroot/icons/icon512x512.png | Bin .../wwwroot/icons/icon64x64.png | Bin .../Elib2EbookWeb}/wwwroot/manifest.json | 0 tests/Core.Tests/Core.Tests.csproj | 31 ++ .../Extensions/HtmlDocumentExtensionsTests.cs | 79 +++++ .../Extensions/ImageFormatExtensionsTests.cs | 24 ++ .../Extensions/StreamExtensionTests.cs | 33 ++ .../StringBuilderExtensionsTests.cs | 15 + .../Extensions/StringExtensionsTests.cs | 105 ++++++ .../Extensions/UriExtensionTests.cs | 72 ++++ .../Core.Tests/Logic/Builders/BuilderTests.cs | 41 +++ tests/Core.Tests/Misc/MiscTests.cs | 77 ++++ tests/Core.Tests/Misc/TempFolderTests.cs | 106 ++++++ tests/Core.Tests/Types/Book/BookTypeTests.cs | 165 +++++++++ 521 files changed, 8908 insertions(+), 6334 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/workflows/codeql.yml delete mode 100644 Core/Core.csproj delete mode 100644 Core/Types/AuthorToday/AuthorTodayAuthResponse.cs delete mode 100644 Core/Types/AuthorToday/AuthorTodayBookDetails.cs delete mode 100644 Core/Types/AuthorToday/AuthorTodayChapter.cs delete mode 100644 Core/Types/AuthorToday/AuthorTodayImage.cs delete mode 100644 Core/Types/AuthorToday/AuthorTodayUser.cs delete mode 100644 Core/Types/BookYandex/BooksYandexAudioBitrate.cs delete mode 100644 Core/Types/BookYandex/BooksYandexAudioOffline.cs delete mode 100644 Core/Types/BookYandex/BooksYandexAudioTrack.cs delete mode 100644 Core/Types/BookYandex/BooksYandexAuthor.cs delete mode 100644 Core/Types/BookYandex/BooksYandexBook.cs delete mode 100644 Core/Types/BookYandex/BooksYandexComicContent.cs delete mode 100644 Core/Types/BookYandex/BooksYandexComicMetadata.cs delete mode 100644 Core/Types/BookYandex/BooksYandexComicPage.cs delete mode 100644 Core/Types/BookYandex/BooksYandexCover.cs delete mode 100644 Core/Types/BookYandex/BooksYandexEpisode.cs delete mode 100644 Core/Types/BookYandex/BooksYandexEpisodes.cs delete mode 100644 Core/Types/BookYandex/BooksYandexPlaylist.cs delete mode 100644 Core/Types/BookYandex/BooksYandexResponse.cs delete mode 100644 Core/Types/BookYandex/BooksYandexUri.cs delete mode 100644 Core/Types/Bookriver/BookRiverApiResponse.cs delete mode 100644 Core/Types/Bookriver/BookRiverAuthResponse.cs delete mode 100644 Core/Types/Bookriver/BookRiverBook.cs delete mode 100644 Core/Types/Bookriver/BookRiverChapter.cs delete mode 100644 Core/Types/Bookriver/BookRiverChapterContent.cs delete mode 100644 Core/Types/Bookriver/BookRiverCurrentBook.cs delete mode 100644 Core/Types/Bookstab/BookstabApiResponse.cs delete mode 100644 Core/Types/Bookstab/BookstabBook.cs delete mode 100644 Core/Types/Bookstab/BookstabChapter.cs delete mode 100644 Core/Types/Bookstab/BookstabUser.cs delete mode 100644 Core/Types/Bookstime/BookstimeAuthResponse.cs delete mode 100644 Core/Types/Boosty/BoostyApiResponse.cs delete mode 100644 Core/Types/Boosty/BoostyExtra.cs delete mode 100644 Core/Types/Boosty/BoostyPost.cs delete mode 100644 Core/Types/Common/IdChapter.cs delete mode 100644 Core/Types/Common/UrlChapter.cs delete mode 100644 Core/Types/DarkNovels/DarkNovelsAuthData.cs delete mode 100644 Core/Types/DarkNovels/DarkNovelsAuthToken.cs delete mode 100644 Core/Types/DarkNovels/DarkNovelsChapter.cs delete mode 100644 Core/Types/DarkNovels/DarkNovelsData.cs delete mode 100644 Core/Types/Dreame/DreameApiResponse.cs delete mode 100644 Core/Types/Dreame/DreameCatalog.cs delete mode 100644 Core/Types/Dreame/DreameChapter.cs delete mode 100644 Core/Types/Dreame/DreameNovel.cs delete mode 100644 Core/Types/Dreame/DreamePager.cs delete mode 100644 Core/Types/FanFicus/FanFicusApiResponse.cs delete mode 100644 Core/Types/FanFicus/FanFicusBook.cs delete mode 100644 Core/Types/FanFicus/FanFicusChapter.cs delete mode 100644 Core/Types/FanFicus/FanFicusCreator.cs delete mode 100644 Core/Types/FanFicus/FanFicusImage.cs delete mode 100644 Core/Types/FanFicus/FanFicusPart.cs delete mode 100644 Core/Types/FanFicus/FanFicusUser.cs delete mode 100644 Core/Types/Freedlit/FreedlitApiResponse.cs delete mode 100644 Core/Types/Freedlit/FreedlitApp.cs delete mode 100644 Core/Types/Freedlit/FreedlitAuthError.cs delete mode 100644 Core/Types/Freedlit/FreedlitAuthor.cs delete mode 100644 Core/Types/Freedlit/FreedlitBook.cs delete mode 100644 Core/Types/Freedlit/FreedlitChapter.cs delete mode 100644 Core/Types/Freedlit/FreedlitItemsContent.cs delete mode 100644 Core/Types/Freedlit/FreedlitProps.cs delete mode 100644 Core/Types/Freedlit/FreedlitSuccessResponse.cs delete mode 100644 Core/Types/HotNovelPub/HotNovelPubApiResponse.cs delete mode 100644 Core/Types/HotNovelPub/HotNovelPubAuthorize.cs delete mode 100644 Core/Types/HotNovelPub/HotNovelPubBook.cs delete mode 100644 Core/Types/HotNovelPub/HotNovelPubBookResponse.cs delete mode 100644 Core/Types/HotNovelPub/HotNovelPubChapter.cs delete mode 100644 Core/Types/Librebook/LibrebookAuthResponse.cs delete mode 100644 Core/Types/Litexit/LitexitChapter.cs delete mode 100644 Core/Types/Litexit/LitexitUser.cs delete mode 100644 Core/Types/Litgorod/LitgorodAuthResponse.cs delete mode 100644 Core/Types/Litgorod/LitgorodBookPart.cs delete mode 100644 Core/Types/Litgorod/LitgorodChapter.cs delete mode 100644 Core/Types/Litmarket/AuthResponse.cs delete mode 100644 Core/Types/Litmarket/Block.cs delete mode 100644 Core/Types/Litmarket/Chunk.cs delete mode 100644 Core/Types/Litmarket/LBook.cs delete mode 100644 Core/Types/Litmarket/Mod.cs delete mode 100644 Core/Types/Litmarket/Response.cs delete mode 100644 Core/Types/Litnet/LitnetAuthResponse.cs delete mode 100644 Core/Types/Litnet/LitnetBookResponse.cs delete mode 100644 Core/Types/Litnet/LitnetChapterResponse.cs delete mode 100644 Core/Types/Litnet/LitnetContentsResponse.cs delete mode 100644 Core/Types/Litres/LitresPayload.cs delete mode 100644 Core/Types/Litres/Requests/LitresAuthData.cs delete mode 100644 Core/Types/Litres/Requests/LitresBrowseArtsData.cs delete mode 100644 Core/Types/Litres/Requests/LitresRequestBase.cs delete mode 100644 Core/Types/Litres/Response/LitresArt.cs delete mode 100644 Core/Types/Litres/Response/LitresAuthResponseData.cs delete mode 100644 Core/Types/Litres/Response/LitresFile.cs delete mode 100644 Core/Types/Litres/Response/LitresFiles.cs delete mode 100644 Core/Types/Litres/Response/LitresMe.cs delete mode 100644 Core/Types/Litres/Response/LitresPartnerSubscriptions.cs delete mode 100644 Core/Types/Litres/Response/LitresPerson.cs delete mode 100644 Core/Types/Litres/Response/LitresResponse.cs delete mode 100644 Core/Types/Litres/Response/LitresSeria.cs delete mode 100644 Core/Types/Litres/Response/LitresStaticResponse.cs delete mode 100644 Core/Types/Litres/Response/LitresSubscription.cs delete mode 100644 Core/Types/Litsovet/LitsovetLoginResponse.cs delete mode 100644 Core/Types/MangaLib/MangaLibPg.cs delete mode 100644 Core/Types/MyBook/MyBookAuth.cs delete mode 100644 Core/Types/MyBook/MyBookAuthor.cs delete mode 100644 Core/Types/MyBook/MyBookBook.cs delete mode 100644 Core/Types/MyBook/MyBookFile.cs delete mode 100644 Core/Types/MyBook/MyBookMapFile.cs delete mode 100644 Core/Types/Neobook/NeobookAttachment.cs delete mode 100644 Core/Types/Neobook/NeobookAuth.cs delete mode 100644 Core/Types/Neobook/NeobookBook.cs delete mode 100644 Core/Types/Neobook/NeobookChapter.cs delete mode 100644 Core/Types/Neobook/NeobookChapterData.cs delete mode 100644 Core/Types/Neobook/NeobookLogin.cs delete mode 100644 Core/Types/Neobook/NeobookPostData.cs delete mode 100644 Core/Types/Neobook/NeobookTocChapter.cs delete mode 100644 Core/Types/Neobook/NeobookUser.cs delete mode 100644 Core/Types/Ranobe/RanobeBook.cs delete mode 100644 Core/Types/Ranobe/RanobeChapter.cs delete mode 100644 Core/Types/Ranobe/RanobeChapterContent.cs delete mode 100644 Core/Types/Ranobe/RanobeChapterShort.cs delete mode 100644 Core/Types/Ranobe/RanobeImage.cs delete mode 100644 Core/Types/RanobeHub/RanobeHubApiResponse.cs delete mode 100644 Core/Types/RanobeHub/RanobeHubChapter.cs delete mode 100644 Core/Types/RanobeHub/RanobeHubVolume.cs delete mode 100644 Core/Types/RanobeNovels/RanobeNovelsChapter.cs delete mode 100644 Core/Types/RanobeOvh/RanobeOvhBranch.cs delete mode 100644 Core/Types/RanobeOvh/RanobeOvhChapterFull.cs delete mode 100644 Core/Types/RanobeOvh/RanobeOvhManga.cs delete mode 100644 Core/Types/RanobeOvh/RanobeOvhMangaName.cs delete mode 100644 Core/Types/RanobeOvh/RanobeOvhPage.cs delete mode 100644 Core/Types/RanobeOvh/RanoveOvhTranslator.cs delete mode 100644 Core/Types/Ranobes/RanobesCookie.cs delete mode 100644 Core/Types/Renovels/RenovelsApiResponse.cs delete mode 100644 Core/Types/Renovels/RenovelsAuthResponse.cs delete mode 100644 Core/Types/Renovels/RenovelsBranch.cs delete mode 100644 Core/Types/Renovels/RenovelsChapter.cs delete mode 100644 Core/Types/Renovels/RenovelsContent.cs delete mode 100644 Core/Types/Renovels/RenovelsPage.cs delete mode 100644 Core/Types/Renovels/RenovelsPublisher.cs delete mode 100644 Core/Types/Renovels/RenovelsTocResponse.cs delete mode 100644 Core/Types/Rulate/RulateAuthResponse.cs delete mode 100644 Core/Types/Rulate/RulateChapter.cs delete mode 100644 Core/Types/SocialLib/LibSocialToken.cs delete mode 100644 Core/Types/SocialLib/RanobeLibBookDetail.cs delete mode 100644 Core/Types/SocialLib/SocialLibBookChapters.cs delete mode 100644 Core/Types/SocialLib/SocialLibChapter.cs delete mode 100644 Core/Types/SocialLib/SocialLibChapterOld.cs delete mode 100644 Core/Types/SocialLib/SocialLibChapters.cs delete mode 100644 Core/Types/SocialLib/User.cs delete mode 100644 Core/Types/SocialLib/WindowData.cs delete mode 100644 Core/Types/StrokiMts/StrokiMtsApiMultiResponse.cs delete mode 100644 Core/Types/StrokiMts/StrokiMtsApiResponse.cs delete mode 100644 Core/Types/StrokiMts/StrokiMtsAuthor.cs delete mode 100644 Core/Types/StrokiMts/StrokiMtsBookItem.cs delete mode 100644 Core/Types/StrokiMts/StrokiMtsContentInfo.cs delete mode 100644 Core/Types/StrokiMts/StrokiMtsFile.cs delete mode 100644 Core/Types/StrokiMts/StrokiMtsFileUrl.cs delete mode 100644 Core/Types/StrokiMts/StrokiMtsFiles.cs delete mode 100644 Core/Types/StrokiMts/StrokiMtsMultiItem.cs delete mode 100644 Core/Types/Wattpad/WattpadInfo.cs delete mode 100644 Core/Types/Wattpad/WattpadMeta.cs delete mode 100644 Core/Types/Wattpad/WattpadUser.cs delete mode 100644 Core/Types/WuxiaWorld/WuxiaWorldToc.cs create mode 100644 Directory.Build.props create mode 100644 Directory.Packages.props delete mode 100644 Elib2EbookCli/ConsoleLogger.cs delete mode 100644 Elib2EbookCli/Elib2EbookCli.csproj delete mode 100644 Elib2EbookWeb/Components/App.razor delete mode 100644 Elib2EbookWeb/Components/PWA.razor delete mode 100644 Elib2EbookWeb/Components/ShortBook.razor delete mode 100644 Elib2EbookWeb/Elib2EbookWeb.csproj delete mode 100644 Elib2EbookWeb/Misc/SbWriter.cs delete mode 100644 Elib2EbookWeb/Properties/launchSettings.json rename {Core => src/Core}/Configs/BookGetterConfig.cs (73%) rename {Core => src/Core}/Configs/Options.cs (74%) create mode 100644 src/Core/Core.csproj rename {Core => src/Core}/Extensions/FileProviderExtensions.cs (70%) rename {Core => src/Core}/Extensions/HtmlDocumentExtensions.cs (70%) rename {Core => src/Core}/Extensions/HttpClientExtensions.cs (64%) rename {Core => src/Core}/Extensions/ImageFormatExtensions.cs (77%) rename {Core => src/Core}/Extensions/StreamExtension.cs (67%) rename {Core => src/Core}/Extensions/StringBuilderExtensions.cs (79%) rename {Core => src/Core}/Extensions/StringExtensions.cs (70%) rename {Core => src/Core}/Extensions/UriExtension.cs (62%) rename {Core => src/Core}/External/EpubSharp.deps.json (100%) rename {Core => src/Core}/External/EpubSharp.dll (100%) rename {Core => src/Core}/External/EpubSharp.pdb (100%) rename {Core => src/Core}/Logic/BookNameBuilder.cs (72%) rename {Core => src/Core}/Logic/Builders/AdditionaFileBuilder.cs (80%) rename {Core => src/Core}/Logic/Builders/BuilderBase.cs (75%) rename {Core => src/Core}/Logic/Builders/CbzBuilder.cs (70%) rename {Core => src/Core}/Logic/Builders/EpubBuilder.cs (79%) rename {Core => src/Core}/Logic/Builders/Fb2Builder.cs (70%) rename {Core => src/Core}/Logic/Builders/JsonBuilder.cs (69%) rename {Core => src/Core}/Logic/Builders/JsonLiteBuilder.cs (72%) rename {Core => src/Core}/Logic/Builders/TxtBuilder.cs (68%) rename {Core => src/Core}/Logic/FileProvider.cs (71%) rename {Core => src/Core}/Logic/Getters/AcomicsGetter.cs (73%) rename {Core => src/Core}/Logic/Getters/AuthorTodayGetter.cs (74%) rename {Core => src/Core}/Logic/Getters/BookInBookGetter.cs (72%) rename {Core => src/Core}/Logic/Getters/BookRiverGetter.cs (73%) rename {Core => src/Core}/Logic/Getters/BookinistGetter.cs (72%) rename {Core => src/Core}/Logic/Getters/BooksYandex/BooksYandexBooksGetter.cs (66%) rename {Core => src/Core}/Logic/Getters/BooksYandex/BooksYandexComicsGetter.cs (65%) rename {Core => src/Core}/Logic/Getters/BooksYandex/BooksYandexGetterBase.cs (74%) rename {Core => src/Core}/Logic/Getters/BookstabGetter.cs (77%) rename {Core => src/Core}/Logic/Getters/BookstimeGetter.cs (78%) rename {Core => src/Core}/Logic/Getters/BookurukGetter.cs (73%) rename {Core => src/Core}/Logic/Getters/BoostyGetter.cs (73%) rename {Core => src/Core}/Logic/Getters/BoovelGetter.cs (83%) rename {Core => src/Core}/Logic/Getters/DarkNovelsGetter.cs (73%) rename {Core => src/Core}/Logic/Getters/DesuGetter.cs (75%) rename {Core => src/Core}/Logic/Getters/DreameGetter.cs (73%) rename {Core => src/Core}/Logic/Getters/FanFicusGetter.cs (74%) rename {Core => src/Core}/Logic/Getters/Fb2Top/Fb2TopGetter.cs (68%) rename {Core => src/Core}/Logic/Getters/Fb2Top/Fb2TopGetterBase.cs (69%) rename {Core => src/Core}/Logic/Getters/Fb2Top/LadyLibGetter.cs (68%) rename {Core => src/Core}/Logic/Getters/FicbookGetter.cs (74%) rename {Core => src/Core}/Logic/Getters/FictionBookGetter.cs (73%) rename {Core => src/Core}/Logic/Getters/FreedlitGetter.cs (71%) rename {Core => src/Core}/Logic/Getters/Freedom/BookHamsterGetter.cs (84%) rename {Core => src/Core}/Logic/Getters/Freedom/FreedomGetter.cs (85%) rename {Core => src/Core}/Logic/Getters/Freedom/FreedomGetterBase.cs (69%) rename {Core => src/Core}/Logic/Getters/GetterBase.cs (69%) rename {Core => src/Core}/Logic/Getters/HogwartsNetGetter.cs (70%) rename {Core => src/Core}/Logic/Getters/HotNovelPub/EzNovelsGetter.cs (71%) rename {Core => src/Core}/Logic/Getters/HotNovelPub/HotNovelPubGetter.cs (70%) rename {Core => src/Core}/Logic/Getters/HotNovelPub/HotNovelPubGetterBase.cs (79%) rename {Core => src/Core}/Logic/Getters/HotNovelPub/LaNovelsGetter.cs (71%) rename {Core => src/Core}/Logic/Getters/HotNovelPub/LightNovelDailyGetter.cs (70%) rename {Core => src/Core}/Logic/Getters/HubBookGetter.cs (77%) rename {Core => src/Core}/Logic/Getters/IGramGetter.cs (70%) rename {Core => src/Core}/Logic/Getters/JaomixGetter.cs (74%) rename {Core => src/Core}/Logic/Getters/LibSocial/HentaiLibGetter.cs (88%) rename {Core => src/Core}/Logic/Getters/LibSocial/MangaLibGetter.cs (90%) rename {Core => src/Core}/Logic/Getters/LibSocial/MangaLibGetterBase.cs (72%) rename {Core => src/Core}/Logic/Getters/LibSocial/MangaLibOrgGetter.cs (89%) rename {Core => src/Core}/Logic/Getters/LibSocial/NewLibSocialGetterBase.cs (76%) rename {Core => src/Core}/Logic/Getters/LibSocial/RanobeLibGetter.cs (63%) rename {Core => src/Core}/Logic/Getters/LibSocial/SlashLibGetter.cs (65%) rename {Core => src/Core}/Logic/Getters/LibboxGetter.cs (75%) rename {Core => src/Core}/Logic/Getters/LibkingGetter.cs (69%) rename {Core => src/Core}/Logic/Getters/LibrebookGetter.cs (71%) rename {Core => src/Core}/Logic/Getters/LibstGetter.cs (67%) rename {Core => src/Core}/Logic/Getters/LitLifeGetter.cs (75%) rename {Core => src/Core}/Logic/Getters/LitgorodGetter.cs (76%) rename {Core => src/Core}/Logic/Getters/LitmarketGetter.cs (74%) rename {Core => src/Core}/Logic/Getters/LitmirGetter.cs (73%) rename {Core => src/Core}/Logic/Getters/Litnet/BooknetComGetter.cs (67%) rename {Core => src/Core}/Logic/Getters/Litnet/BooknetUaGetter.cs (68%) rename {Core => src/Core}/Logic/Getters/Litnet/LitnetGetter.cs (69%) rename {Core => src/Core}/Logic/Getters/Litnet/LitnetGetterBase.cs (73%) rename {Core => src/Core}/Logic/Getters/LitresGetter.cs (63%) rename {Core => src/Core}/Logic/Getters/LitsovetGetter.cs (69%) rename {Core => src/Core}/Logic/Getters/MangaMammyGetter.cs (78%) rename {Core => src/Core}/Logic/Getters/MirKnigGetter.cs (72%) rename {Core => src/Core}/Logic/Getters/MyBookGetter.cs (74%) rename {Core => src/Core}/Logic/Getters/NeobookGetter.cs (63%) rename {Core => src/Core}/Logic/Getters/NovelTranslateGetter.cs (76%) rename {Core => src/Core}/Logic/Getters/NovelhallGetter.cs (76%) rename {Core => src/Core}/Logic/Getters/Novelxo/NovelxoGetter.cs (68%) rename {Core => src/Core}/Logic/Getters/Novelxo/NovelxoGetterBase.cs (75%) rename {Core => src/Core}/Logic/Getters/Novelxo/NovelxoRuGetter.cs (67%) rename {Core => src/Core}/Logic/Getters/OnlineKnigiGetter.cs (73%) rename {Core => src/Core}/Logic/Getters/ProdamanGetter.cs (76%) rename {Core => src/Core}/Logic/Getters/RanobeGetter.cs (83%) rename {Core => src/Core}/Logic/Getters/RanobeHubGetter.cs (75%) rename {Core => src/Core}/Logic/Getters/RanobeNovelsGetter.cs (73%) rename {Core => src/Core}/Logic/Getters/RanobeOvh/MangaOvhGetter.cs (76%) rename {Core => src/Core}/Logic/Getters/RanobeOvh/NovelOvhGetter.cs (64%) rename {Core => src/Core}/Logic/Getters/RanobeOvh/RanobeOvhGetterBase.cs (80%) rename {Core => src/Core}/Logic/Getters/RanobesComGetter.cs (74%) rename {Core => src/Core}/Logic/Getters/ReadMangaGetter.cs (78%) rename {Core => src/Core}/Logic/Getters/ReadliGetter.cs (75%) rename {Core => src/Core}/Logic/Getters/Renovels/RemangaGetter.cs (73%) rename {Core => src/Core}/Logic/Getters/Renovels/RenovelsGetter.cs (79%) rename {Core => src/Core}/Logic/Getters/Renovels/RenovelsGetterBase.cs (78%) rename {Core => src/Core}/Logic/Getters/RomfantGetter.cs (73%) rename {Core => src/Core}/Logic/Getters/RoyalRoadGetter.cs (77%) rename {Core => src/Core}/Logic/Getters/Rulate/BllateGetter.cs (88%) rename {Core => src/Core}/Logic/Getters/Rulate/ErolateGetter.cs (77%) rename {Core => src/Core}/Logic/Getters/Rulate/MlateGetter.cs (78%) rename {Core => src/Core}/Logic/Getters/Rulate/RulateGetter.cs (78%) rename {Core => src/Core}/Logic/Getters/Rulate/RulateGetterBase.cs (70%) rename {Core => src/Core}/Logic/Getters/SamlibGetter.cs (76%) rename {Core => src/Core}/Logic/Getters/StrokiMtsGetter.cs (71%) rename {Core => src/Core}/Logic/Getters/TopLiba/BiglibaGetter.cs (60%) rename {Core => src/Core}/Logic/Getters/TopLiba/TopLibaGetter.cs (57%) rename {Core => src/Core}/Logic/Getters/TopLiba/TopLibaGetterBase.cs (72%) rename {Core => src/Core}/Logic/Getters/TwilightRussiaGetter.cs (71%) rename {Core => src/Core}/Logic/Getters/WattpadGetter.cs (77%) rename {Core => src/Core}/Logic/Getters/WuxiaWorldGetter.cs (71%) rename {Core => src/Core}/Logic/Getters/YounettranslateGetter.cs (75%) rename {Core => src/Core}/Misc/AdditionalTypeEnum.cs (60%) rename {Core => src/Core}/Misc/BuilderProvider.cs (83%) rename {Core => src/Core}/Misc/GetterProvider.cs (75%) rename {Core => src/Core}/Misc/TempFolder/TempFolder.cs (65%) rename {Core => src/Core}/Misc/TempFolder/TempFolderFactory.cs (69%) rename {Core => src/Core}/Patterns/ChapterPattern.xhtml (59%) rename {Core => src/Core}/Patterns/Roboto-Italic.ttf (100%) rename {Core => src/Core}/Patterns/Roboto-Regular.ttf (100%) rename {Core => src/Core}/Patterns/stylesheet.css (68%) create mode 100644 src/Core/Types/AuthorToday/AuthorTodayAuthResponse.cs create mode 100644 src/Core/Types/AuthorToday/AuthorTodayBookDetails.cs create mode 100644 src/Core/Types/AuthorToday/AuthorTodayChapter.cs create mode 100644 src/Core/Types/AuthorToday/AuthorTodayImage.cs create mode 100644 src/Core/Types/AuthorToday/AuthorTodayUser.cs rename {Core => src/Core}/Types/Book/AdditionalFileCollection.cs (58%) rename {Core => src/Core}/Types/Book/Author.cs (59%) rename {Core => src/Core}/Types/Book/Book.cs (87%) rename {Core => src/Core}/Types/Book/Chapter.cs (85%) rename {Core => src/Core}/Types/Book/Seria.cs (82%) create mode 100644 src/Core/Types/BookYandex/BooksYandexAudioBitrate.cs create mode 100644 src/Core/Types/BookYandex/BooksYandexAudioOffline.cs create mode 100644 src/Core/Types/BookYandex/BooksYandexAudioTrack.cs create mode 100644 src/Core/Types/BookYandex/BooksYandexAuthor.cs create mode 100644 src/Core/Types/BookYandex/BooksYandexBook.cs create mode 100644 src/Core/Types/BookYandex/BooksYandexComicContent.cs create mode 100644 src/Core/Types/BookYandex/BooksYandexComicMetadata.cs create mode 100644 src/Core/Types/BookYandex/BooksYandexComicPage.cs create mode 100644 src/Core/Types/BookYandex/BooksYandexCover.cs create mode 100644 src/Core/Types/BookYandex/BooksYandexEpisode.cs create mode 100644 src/Core/Types/BookYandex/BooksYandexEpisodes.cs create mode 100644 src/Core/Types/BookYandex/BooksYandexPlaylist.cs create mode 100644 src/Core/Types/BookYandex/BooksYandexResponse.cs create mode 100644 src/Core/Types/BookYandex/BooksYandexUri.cs create mode 100644 src/Core/Types/Bookriver/BookRiverApiResponse.cs create mode 100644 src/Core/Types/Bookriver/BookRiverAuthResponse.cs create mode 100644 src/Core/Types/Bookriver/BookRiverBook.cs create mode 100644 src/Core/Types/Bookriver/BookRiverChapter.cs create mode 100644 src/Core/Types/Bookriver/BookRiverChapterContent.cs create mode 100644 src/Core/Types/Bookriver/BookRiverCurrentBook.cs create mode 100644 src/Core/Types/Bookstab/BookstabApiResponse.cs create mode 100644 src/Core/Types/Bookstab/BookstabBook.cs create mode 100644 src/Core/Types/Bookstab/BookstabChapter.cs create mode 100644 src/Core/Types/Bookstab/BookstabUser.cs create mode 100644 src/Core/Types/Bookstime/BookstimeAuthResponse.cs create mode 100644 src/Core/Types/Boosty/BoostyApiResponse.cs create mode 100644 src/Core/Types/Boosty/BoostyExtra.cs create mode 100644 src/Core/Types/Boosty/BoostyPost.cs create mode 100644 src/Core/Types/Common/IdChapter.cs rename {Core => src/Core}/Types/Common/TempFile.cs (69%) create mode 100644 src/Core/Types/Common/UrlChapter.cs create mode 100644 src/Core/Types/DarkNovels/DarkNovelsAuthData.cs create mode 100644 src/Core/Types/DarkNovels/DarkNovelsAuthToken.cs create mode 100644 src/Core/Types/DarkNovels/DarkNovelsChapter.cs create mode 100644 src/Core/Types/DarkNovels/DarkNovelsData.cs create mode 100644 src/Core/Types/Dreame/DreameApiResponse.cs create mode 100644 src/Core/Types/Dreame/DreameCatalog.cs create mode 100644 src/Core/Types/Dreame/DreameChapter.cs create mode 100644 src/Core/Types/Dreame/DreameNovel.cs create mode 100644 src/Core/Types/Dreame/DreamePager.cs create mode 100644 src/Core/Types/FanFicus/FanFicusApiResponse.cs create mode 100644 src/Core/Types/FanFicus/FanFicusBook.cs create mode 100644 src/Core/Types/FanFicus/FanFicusChapter.cs create mode 100644 src/Core/Types/FanFicus/FanFicusCreator.cs create mode 100644 src/Core/Types/FanFicus/FanFicusImage.cs create mode 100644 src/Core/Types/FanFicus/FanFicusPart.cs create mode 100644 src/Core/Types/FanFicus/FanFicusUser.cs create mode 100644 src/Core/Types/Freedlit/FreedlitApiResponse.cs create mode 100644 src/Core/Types/Freedlit/FreedlitApp.cs create mode 100644 src/Core/Types/Freedlit/FreedlitAuthError.cs create mode 100644 src/Core/Types/Freedlit/FreedlitAuthor.cs create mode 100644 src/Core/Types/Freedlit/FreedlitBook.cs create mode 100644 src/Core/Types/Freedlit/FreedlitChapter.cs create mode 100644 src/Core/Types/Freedlit/FreedlitItemsContent.cs create mode 100644 src/Core/Types/Freedlit/FreedlitProps.cs create mode 100644 src/Core/Types/Freedlit/FreedlitSuccessResponse.cs create mode 100644 src/Core/Types/HotNovelPub/HotNovelPubApiResponse.cs create mode 100644 src/Core/Types/HotNovelPub/HotNovelPubAuthorize.cs create mode 100644 src/Core/Types/HotNovelPub/HotNovelPubBook.cs create mode 100644 src/Core/Types/HotNovelPub/HotNovelPubBookResponse.cs create mode 100644 src/Core/Types/HotNovelPub/HotNovelPubChapter.cs create mode 100644 src/Core/Types/Librebook/LibrebookAuthResponse.cs create mode 100644 src/Core/Types/Litexit/LitexitChapter.cs create mode 100644 src/Core/Types/Litexit/LitexitUser.cs create mode 100644 src/Core/Types/Litgorod/LitgorodAuthResponse.cs create mode 100644 src/Core/Types/Litgorod/LitgorodBookPart.cs create mode 100644 src/Core/Types/Litgorod/LitgorodChapter.cs create mode 100644 src/Core/Types/Litmarket/AuthResponse.cs create mode 100644 src/Core/Types/Litmarket/Block.cs create mode 100644 src/Core/Types/Litmarket/Chunk.cs create mode 100644 src/Core/Types/Litmarket/LBook.cs create mode 100644 src/Core/Types/Litmarket/Mod.cs create mode 100644 src/Core/Types/Litmarket/Response.cs create mode 100644 src/Core/Types/Litnet/LitnetAuthResponse.cs create mode 100644 src/Core/Types/Litnet/LitnetBookResponse.cs create mode 100644 src/Core/Types/Litnet/LitnetChapterResponse.cs create mode 100644 src/Core/Types/Litnet/LitnetContentsResponse.cs rename {Core => src/Core}/Types/Litres/LitresBook.cs (56%) create mode 100644 src/Core/Types/Litres/LitresPayload.cs rename {Core => src/Core}/Types/Litres/LitresTarget.cs (66%) create mode 100644 src/Core/Types/Litres/Requests/LitresAuthData.cs rename {Core => src/Core}/Types/Litres/Requests/LitresAuthRequest.cs (59%) create mode 100644 src/Core/Types/Litres/Requests/LitresBrowseArtsData.cs rename {Core => src/Core}/Types/Litres/Requests/LitresBrowseArtsRequest.cs (62%) create mode 100644 src/Core/Types/Litres/Requests/LitresRequestBase.cs create mode 100644 src/Core/Types/Litres/Response/LitresArt.cs rename {Core => src/Core}/Types/Litres/Response/LitresArtTypeEnum.cs (70%) create mode 100644 src/Core/Types/Litres/Response/LitresAuthResponseData.cs create mode 100644 src/Core/Types/Litres/Response/LitresFile.cs create mode 100644 src/Core/Types/Litres/Response/LitresFiles.cs create mode 100644 src/Core/Types/Litres/Response/LitresMe.cs create mode 100644 src/Core/Types/Litres/Response/LitresPartnerSubscriptions.cs create mode 100644 src/Core/Types/Litres/Response/LitresPerson.cs create mode 100644 src/Core/Types/Litres/Response/LitresResponse.cs create mode 100644 src/Core/Types/Litres/Response/LitresSeria.cs create mode 100644 src/Core/Types/Litres/Response/LitresStaticResponse.cs create mode 100644 src/Core/Types/Litres/Response/LitresSubscription.cs create mode 100644 src/Core/Types/Litsovet/LitsovetLoginResponse.cs create mode 100644 src/Core/Types/MangaLib/MangaLibPg.cs create mode 100644 src/Core/Types/MyBook/MyBookAuth.cs create mode 100644 src/Core/Types/MyBook/MyBookAuthor.cs create mode 100644 src/Core/Types/MyBook/MyBookBook.cs create mode 100644 src/Core/Types/MyBook/MyBookFile.cs create mode 100644 src/Core/Types/MyBook/MyBookMapFile.cs create mode 100644 src/Core/Types/Neobook/NeobookAttachment.cs create mode 100644 src/Core/Types/Neobook/NeobookAuth.cs create mode 100644 src/Core/Types/Neobook/NeobookBook.cs create mode 100644 src/Core/Types/Neobook/NeobookChapter.cs create mode 100644 src/Core/Types/Neobook/NeobookChapterData.cs create mode 100644 src/Core/Types/Neobook/NeobookLogin.cs create mode 100644 src/Core/Types/Neobook/NeobookPostData.cs create mode 100644 src/Core/Types/Neobook/NeobookTocChapter.cs create mode 100644 src/Core/Types/Neobook/NeobookUser.cs create mode 100644 src/Core/Types/Ranobe/RanobeBook.cs create mode 100644 src/Core/Types/Ranobe/RanobeChapter.cs create mode 100644 src/Core/Types/Ranobe/RanobeChapterContent.cs create mode 100644 src/Core/Types/Ranobe/RanobeChapterShort.cs create mode 100644 src/Core/Types/Ranobe/RanobeImage.cs create mode 100644 src/Core/Types/RanobeHub/RanobeHubApiResponse.cs create mode 100644 src/Core/Types/RanobeHub/RanobeHubChapter.cs create mode 100644 src/Core/Types/RanobeHub/RanobeHubVolume.cs create mode 100644 src/Core/Types/RanobeNovels/RanobeNovelsChapter.cs create mode 100644 src/Core/Types/RanobeOvh/RanobeOvhBranch.cs create mode 100644 src/Core/Types/RanobeOvh/RanobeOvhChapterFull.cs create mode 100644 src/Core/Types/RanobeOvh/RanobeOvhManga.cs create mode 100644 src/Core/Types/RanobeOvh/RanobeOvhMangaName.cs create mode 100644 src/Core/Types/RanobeOvh/RanobeOvhPage.cs create mode 100644 src/Core/Types/RanobeOvh/RanoveOvhTranslator.cs create mode 100644 src/Core/Types/Ranobes/RanobesCookie.cs create mode 100644 src/Core/Types/Renovels/RenovelsApiResponse.cs create mode 100644 src/Core/Types/Renovels/RenovelsAuthResponse.cs create mode 100644 src/Core/Types/Renovels/RenovelsBranch.cs create mode 100644 src/Core/Types/Renovels/RenovelsChapter.cs create mode 100644 src/Core/Types/Renovels/RenovelsContent.cs create mode 100644 src/Core/Types/Renovels/RenovelsPage.cs create mode 100644 src/Core/Types/Renovels/RenovelsPublisher.cs create mode 100644 src/Core/Types/Renovels/RenovelsTocResponse.cs create mode 100644 src/Core/Types/Rulate/RulateAuthResponse.cs create mode 100644 src/Core/Types/Rulate/RulateChapter.cs create mode 100644 src/Core/Types/SocialLib/LibSocialToken.cs create mode 100644 src/Core/Types/SocialLib/RanobeLibBookDetail.cs create mode 100644 src/Core/Types/SocialLib/SocialLibBookChapters.cs create mode 100644 src/Core/Types/SocialLib/SocialLibChapter.cs create mode 100644 src/Core/Types/SocialLib/SocialLibChapterOld.cs create mode 100644 src/Core/Types/SocialLib/SocialLibChapters.cs create mode 100644 src/Core/Types/SocialLib/User.cs create mode 100644 src/Core/Types/SocialLib/WindowData.cs create mode 100644 src/Core/Types/StrokiMts/StrokiMtsApiMultiResponse.cs create mode 100644 src/Core/Types/StrokiMts/StrokiMtsApiResponse.cs create mode 100644 src/Core/Types/StrokiMts/StrokiMtsAuthor.cs create mode 100644 src/Core/Types/StrokiMts/StrokiMtsBookItem.cs create mode 100644 src/Core/Types/StrokiMts/StrokiMtsContentInfo.cs create mode 100644 src/Core/Types/StrokiMts/StrokiMtsFile.cs create mode 100644 src/Core/Types/StrokiMts/StrokiMtsFileUrl.cs create mode 100644 src/Core/Types/StrokiMts/StrokiMtsFiles.cs create mode 100644 src/Core/Types/StrokiMts/StrokiMtsMultiItem.cs create mode 100644 src/Core/Types/Wattpad/WattpadInfo.cs create mode 100644 src/Core/Types/Wattpad/WattpadMeta.cs rename {Core => src/Core}/Types/Wattpad/WattpadPart.cs (51%) create mode 100644 src/Core/Types/Wattpad/WattpadUser.cs create mode 100644 src/Core/Types/WuxiaWorld/WuxiaWorldToc.cs create mode 100644 src/Elib2EbookCli/ConsoleLogger.cs rename {Elib2EbookCli => src/Elib2EbookCli}/Dockerfile (63%) create mode 100644 src/Elib2EbookCli/Elib2EbookCli.csproj rename {Elib2EbookCli => src/Elib2EbookCli}/Program.cs (62%) create mode 100644 src/Elib2EbookWeb/Components/App.razor rename {Elib2EbookWeb => src/Elib2EbookWeb}/Components/BookForm.razor (62%) rename {Elib2EbookWeb => src/Elib2EbookWeb}/Components/Layout/MainLayout.razor (62%) rename {Elib2EbookWeb => src/Elib2EbookWeb}/Components/Layout/MainLayout.razor.css (72%) create mode 100644 src/Elib2EbookWeb/Components/PWA.razor rename {Elib2EbookWeb => src/Elib2EbookWeb}/Components/Pages/Error.razor (67%) rename {Elib2EbookWeb => src/Elib2EbookWeb}/Components/Pages/Home.razor (81%) rename {Elib2EbookWeb => src/Elib2EbookWeb}/Components/Routes.razor (100%) create mode 100644 src/Elib2EbookWeb/Components/ShortBook.razor rename {Elib2EbookWeb => src/Elib2EbookWeb}/Components/_Imports.razor (100%) rename {Elib2EbookWeb => src/Elib2EbookWeb}/Dockerfile (62%) create mode 100644 src/Elib2EbookWeb/Elib2EbookWeb.csproj create mode 100644 src/Elib2EbookWeb/Misc/SbWriter.cs rename {Elib2EbookWeb => src/Elib2EbookWeb}/Program.cs (92%) create mode 100644 src/Elib2EbookWeb/Properties/launchSettings.json rename {Elib2EbookWeb => src/Elib2EbookWeb}/appsettings.Development.json (100%) rename {Elib2EbookWeb => src/Elib2EbookWeb}/appsettings.json (100%) rename {Elib2EbookWeb => src/Elib2EbookWeb}/wwwroot/app.css (94%) rename {Elib2EbookWeb => src/Elib2EbookWeb}/wwwroot/bootstrap/bootstrap.min.css (100%) rename {Elib2EbookWeb => src/Elib2EbookWeb}/wwwroot/bootstrap/bootstrap.min.css.map (100%) rename {Elib2EbookWeb => src/Elib2EbookWeb}/wwwroot/favicon.png (100%) rename {Elib2EbookWeb => src/Elib2EbookWeb}/wwwroot/icons/icon128x128.png (100%) rename {Elib2EbookWeb => src/Elib2EbookWeb}/wwwroot/icons/icon256x256.png (100%) rename {Elib2EbookWeb => src/Elib2EbookWeb}/wwwroot/icons/icon32x32.png (100%) rename {Elib2EbookWeb => src/Elib2EbookWeb}/wwwroot/icons/icon512x512.png (100%) rename {Elib2EbookWeb => src/Elib2EbookWeb}/wwwroot/icons/icon64x64.png (100%) rename {Elib2EbookWeb => src/Elib2EbookWeb}/wwwroot/manifest.json (100%) create mode 100644 tests/Core.Tests/Core.Tests.csproj create mode 100644 tests/Core.Tests/Extensions/HtmlDocumentExtensionsTests.cs create mode 100644 tests/Core.Tests/Extensions/ImageFormatExtensionsTests.cs create mode 100644 tests/Core.Tests/Extensions/StreamExtensionTests.cs create mode 100644 tests/Core.Tests/Extensions/StringBuilderExtensionsTests.cs create mode 100644 tests/Core.Tests/Extensions/StringExtensionsTests.cs create mode 100644 tests/Core.Tests/Extensions/UriExtensionTests.cs create mode 100644 tests/Core.Tests/Logic/Builders/BuilderTests.cs create mode 100644 tests/Core.Tests/Misc/MiscTests.cs create mode 100644 tests/Core.Tests/Misc/TempFolderTests.cs create mode 100644 tests/Core.Tests/Types/Book/BookTypeTests.cs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..8427f385 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,65 @@ +# EditorConfig for Elib2Ebook +# https://editorconfig.org + +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{cs,razor,cshtml}] +indent_size = 4 +indent_style = space + +[*.xml] +indent_size = 2 + +[*.{yml,yaml}] +indent_size = 2 + +[*.json] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.sh] +end_of_line = lf + +[*.{sln,csproj,props}] +indent_size = 4 + +# .NET conventions +[*.cs] +# Sort usings +dotnet_sort_system_directives_first = true +# File-scoped namespaces +dotnet_style_namespace_match_folder = true +# var preferences +csharp_style_var_for_built_in_types = true:warning +csharp_style_var_when_type_is_apparent = true:warning +csharp_style_var_elsewhere = true:silent +# Expression-bodied members +csharp_style_expression_bodied_methods = true:silent +csharp_style_expression_bodied_constructors = true:silent +csharp_style_expression_bodied_properties = true:warning +# Pattern matching +csharp_style_pattern_matching_over_is_with_cast_check = true:warning +csharp_style_pattern_matching_over_as_with_null_check = true:warning +# Null checking +csharp_style_throw_expression = true:warning +csharp_style_conditional_delegate_call = true:warning +# Modifiers +dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning +# Braces +csharp_prefer_braces = true:silent +# Newline +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..a23999d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,32 @@ +--- +name: 🐛 Bug Report +about: Сообщить об ошибке +title: '' +labels: bug +assignees: '' +--- + +## Описание ошибки +Чёткое и краткое описание бага. + +## Шаги для воспроизведения +1. Запустить: `Elib2EbookCli -u '...' -f '...'` +2. ... +3. Вижу ошибку + +## Ожидаемое поведение +Что должно было произойти? + +## Окружение +- **OS:** [например macOS 15, Windows 11, Ubuntu 24.04] +- **Версия .NET:** [если применимо] +- **Версия утилиты:** [--version или тег релиза] +- **Способ запуска:** [Portable / Docker / dotnet run] + +## Логи +``` +Вставьте логи сюда (запустите с --save-temp если нужно) +``` + +## Дополнительная информация +Любая другая информация, которая может помочь. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..7691c821 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Вопросы и обсуждения + url: https://github.com/OnlyFart/Elib2Ebook/discussions + about: Задайте вопрос или предложите обсуждение в Discussions \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..ecd5bc17 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,19 @@ +--- +name: 🚀 Feature Request +about: Предложить новую функцию +title: '' +labels: enhancement +assignees: '' +--- + +## Проблема +Опишите проблему, которую вы хотите решить. Например: *"Мне не хватает поддержки сайта example.com"* + +## Предлагаемое решение +Чёткое описание того, что вы хотите видеть. + +## Альтернативы +Какие альтернативы вы рассматривали? + +## Дополнительная информация +Ссылки, примеры, скриншоты. \ No newline at end of file diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..937667a5 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,48 @@ +name: "CodeQL Analysis" + +on: + push: + branches: [master] + pull_request: + branches: [master] + schedule: + - cron: '0 6 * * 1' # каждый понедельник в 6 утра UTC + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + timeout-minutes: 360 + permissions: + security-events: write + packages: read + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + language: ['csharp'] + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + queries: security-and-quality + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 9.0.x + + - name: Build + run: dotnet build -c release Elib2Ebook.sln + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" \ No newline at end of file diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index f572167c..aca24f56 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -7,50 +7,88 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: 9.0.x - + - name: Restore dependencies - run: dotnet restore + run: dotnet restore Elib2Ebook.sln - name: Build - run: dotnet build --no-restore -c release - + run: dotnet build --no-restore -c release Elib2Ebook.sln + + test: + needs: build + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v5 + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 9.0.x + + - name: Restore dependencies + run: dotnet restore Elib2Ebook.sln + + - name: Build + run: dotnet build --no-restore -c release Elib2Ebook.sln + - name: Test - run: dotnet test --no-build --verbosity normal - + run: dotnet test --no-build -c release --verbosity normal Elib2Ebook.sln + + publish: + needs: test + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v5 + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 9.0.x + + - name: Restore dependencies + run: dotnet restore Elib2Ebook.sln + + - name: Build + run: dotnet build --no-restore -c release Elib2Ebook.sln + - name: Min run: > - dotnet publish -c release -p:PublishDir=../dist/min --no-self-contained - -p:PublishSingleFile=true -p:DebugType=embedded -p:PublishTrimmed=false - -p:EnableCompressionInSingleFile=false --use-current-runtime Elib2EbookCli - + dotnet publish -c release -p:PublishDir=../../dist/min --no-self-contained + -p:PublishSingleFile=true -p:DebugType=embedded -p:PublishTrimmed=false + -p:EnableCompressionInSingleFile=false --use-current-runtime src/Elib2EbookCli/Elib2EbookCli.csproj + - name: Portable run: > - dotnet publish -c release -p:PublishDir=../dist/portable --self-contained + dotnet publish -c release -p:PublishDir=../../dist/portable --self-contained -p:PublishSingleFile=true -p:DebugType=embedded - -p:EnableCompressionInSingleFile=true --use-current-runtime Elib2EbookCli + -p:EnableCompressionInSingleFile=true --use-current-runtime src/Elib2EbookCli/Elib2EbookCli.csproj - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: Elib2Ebook-${{ matrix.os }}-min path: ${{ github.workspace }}/dist/min - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: Elib2Ebook-${{ matrix.os }}-portable path: ${{ github.workspace }}/dist/portable - + check_version: name: Check version - needs: build + needs: test runs-on: ubuntu-latest if: github.ref == 'refs/heads/master' outputs: @@ -59,19 +97,19 @@ jobs: steps: - name: check repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - uses: kzrnm/get-net-sdk-project-versions-action@v2 id: get-version with: - proj-path: Core/Core.csproj + proj-path: src/Core/Core.csproj - name: "get-last-release" id: last_release uses: InsonusK/get-latest-release@v1.1.0 with: myToken: ${{ github.token }} - + push_to_registry: name: push docker image to hub runs-on: ubuntu-latest @@ -89,7 +127,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: check repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: login to docker registry uses: docker/login-action@v3 @@ -106,17 +144,17 @@ jobs: uses: docker/build-push-action@v6 with: platforms: linux/amd64, linux/arm64 - file: "${{ matrix.folder }}/Dockerfile" + file: "src/${{ matrix.folder }}/Dockerfile" push: true tags: ${{secrets.DOCKERHUB_NAMESPACE}}/${{ steps.string.outputs.lowercase }}:${{ needs.check_version.outputs.VERSION }},${{secrets.DOCKERHUB_NAMESPACE}}/${{ steps.string.outputs.lowercase }}:latest - + create_release: needs: check_version runs-on: ubuntu-latest if: ${{ needs.check_version.outputs.LAST_TAG != needs.check_version.outputs.VERSION }} outputs: UPLOAD_URL: ${{ steps.create_release.outputs.upload_url }} - + steps: - name: release uses: actions/create-release@v1 @@ -128,29 +166,29 @@ jobs: tag_name: ${{needs.check_version.outputs.VERSION}} env: GITHUB_TOKEN: ${{ github.token }} - + upload_release: - needs: create_release + needs: [create_release, publish] runs-on: ubuntu-latest strategy: matrix: os: [ ubuntu-latest, macos-latest, windows-latest ] version: [ min, portable ] - + steps: - name: download-artifact-${{ matrix.os }}-${{ matrix.version }} - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: Elib2Ebook-${{ matrix.os }}-${{ matrix.version }} - path: ${{ github.workspace }}/${{ matrix.os }}-${{ matrix.version }} - + path: ${{ github.workspace }}/${{ matrix.os }}-${{ matrix.version }} + - name: Install zip uses: montudor/action-zip@v0.1.0 - + - name: Zip output run: zip -qq -x "*.pdb" -r ../Elib2Ebook-${{ matrix.os }}-${{ matrix.version }}.zip . working-directory: ${{ github.workspace }}/${{ matrix.os }}-${{ matrix.version }} - + - name: upload ${{ matrix.os }}-${{ matrix.version }} asset uses: actions/upload-release-asset@v1 env: @@ -160,4 +198,3 @@ jobs: asset_path: Elib2Ebook-${{ matrix.os }}-${{ matrix.version }}.zip asset_name: Elib2Ebook-${{ matrix.os }}-${{ matrix.version }}.zip asset_content_type: application/zip - diff --git a/.gitignore b/.gitignore index 4ae9628d..63b7b32d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,47 +1,37 @@ -**/bin/* -**/obj/* -**_ReSharper* -**/.sonar/* -Code/**/packages/* -**.suo -**.user -**.dbmdl -**.schemaview -DB/**/sql -**.orig -**.rdl.data -**/StyleCopViolations.xml -_references.js -*_*_*.csproj.swp -*_*_*.csproj -/Code/.vs -/Code/.vscode/ +# .NET +**/bin/ +**/obj/ +*.user +*.suo +*.vs/ +*.DotSettings.user +*.csproj.user + +# IDE +.idea/ +*/.idea/ +.vs/ +.vscode/ +_ReSharper*/ + +# Build +dist/ +.dist/ *.nupkg -**/node_modules/** -**/react-widgets/dist/** -**/react-widgets/*.log -/Code/.idea/.idea.IDEV/.idea/.name -/Code/.idea/.idea.IDEV/.idea/contentModel.xml -/Code/.idea/.idea.IDEV/.idea/modules.xml -/Code/.idea/.idea.IDEV/.idea/vcs.xml -/Code/.idea/.idea.IDEV/.idea/workspace.xml -/Code/.idea/.idea.IDEV/riderModule.iml -/Code/.idea/config/applicationhost.config -/Code/.idea/.idea.AllIn/.idea/.name -/Code/.idea/.idea.AllIn/.idea/contentModel.xml -/Code/.idea/.idea.AllIn/.idea/modules.xml -/Code/.idea/.idea.AllIn/.idea/workspace.xml -/Code/.idea/.idea.AllIn/.idea/vcs.xml -/Code/.idea/.idea.AllIn/riderModule.iml -/Code/.idea -Code/\.idea/\.idea\.AllIn/\.idea/ -.idea -*/.idea/* -**/logs/** -dist -.dist -.vs -.vscode/launch.json -.vscode/tasks.json - -.dotnet/ +*.log +logs/ + +# OS +.DS_Store +Thumbs.db + +# Node (for any future frontend dependencies) +node_modules/ + +# NuGet +packages/ +*.nuget.props +*.nuget.targets + +# dotnet tools +.dotnet/ \ No newline at end of file diff --git a/Core/Core.csproj b/Core/Core.csproj deleted file mode 100644 index 5b9a48af..00000000 --- a/Core/Core.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net9.0 - latestmajor - Core - 2.8.2 - true - - - - - - - - - - - - - - - - - - - Always - - - - - - External\EpubSharp.dll - - - - diff --git a/Core/Types/AuthorToday/AuthorTodayAuthResponse.cs b/Core/Types/AuthorToday/AuthorTodayAuthResponse.cs deleted file mode 100644 index 72d4398e..00000000 --- a/Core/Types/AuthorToday/AuthorTodayAuthResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.AuthorToday; - -public class AuthorTodayAuthResponse { - [JsonPropertyName("message")] - public string Message { get; set; } - - [JsonPropertyName("token")] - public string Token { get; set; } -} \ No newline at end of file diff --git a/Core/Types/AuthorToday/AuthorTodayBookDetails.cs b/Core/Types/AuthorToday/AuthorTodayBookDetails.cs deleted file mode 100644 index f9dfdeec..00000000 --- a/Core/Types/AuthorToday/AuthorTodayBookDetails.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.AuthorToday; - -public class AuthorTodayBookDetails { - [JsonPropertyName("annotation")] - public string Annotation { get; set; } - - [JsonPropertyName("seriesId")] - public long? SeriesId { get; set; } - - [JsonPropertyName("seriesWorkNumber")] - public long? SeriesWorkNumber { get; set; } - - [JsonPropertyName("seriesTitle")] - public string SeriesTitle { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("authorFIO")] - public string AuthorFio { get; set; } - - [JsonPropertyName("coAuthorFIO")] - public string CoAuthorFio { get; set; } - - [JsonPropertyName("coverUrl")] - public string CoverUrl { get; set; } - - [JsonPropertyName("chapters")] - public AuthorTodayChapter[] Chapters { get; set; } - - [JsonPropertyName("id")] - public long Id { get; set; } - - [JsonPropertyName("authorUserName")] - public string AuthorUserName { get; set; } - - [JsonPropertyName("CoAuthorUserName")] - public string CoAuthorUserName { get; set; } - - [JsonPropertyName("galleryImages")] - public AuthorTodayImage[] GalleryImages { get; set; } -} \ No newline at end of file diff --git a/Core/Types/AuthorToday/AuthorTodayChapter.cs b/Core/Types/AuthorToday/AuthorTodayChapter.cs deleted file mode 100644 index 6b1ac2f3..00000000 --- a/Core/Types/AuthorToday/AuthorTodayChapter.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Text.Json.Serialization; - -namespace Core.Types.AuthorToday; - -public class AuthorTodayChapter { - [JsonPropertyName("id")] - public long Id { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("sortOrder")] - public long SortOrder { get; set; } - - [JsonPropertyName("text")] - public string Text { get; set; } - - [JsonPropertyName("key")] - public string Key { get; set; } - - [JsonPropertyName("IsSuccessful")] - public bool IsSuccessful { get; set; } - - [JsonPropertyName("IsDraft")] - public bool IsDraft { get; set; } - - [JsonPropertyName("code")] - public string Code { get; set; } - - public string Decode(string userId, string cert) { - var secret = $"{string.Concat(Key.Reverse())}:{(string.IsNullOrWhiteSpace(userId) ? "Guest" : userId)}:{"FjPg]{2+$8JRvv~("}:{cert}"; - var hashSecret = Convert.ToHexString(MD5.HashData(Encoding.UTF8.GetBytes(secret))); - - using var aes = Aes.Create(); - const int IV_SHIFT = 16; - - var aesKey = Encoding.UTF8.GetBytes(hashSecret)[..IV_SHIFT]; - - aes.Key = aesKey; - aes.IV = aesKey; - - using var ms = new MemoryStream(Convert.FromBase64String(Text)); - using var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read); - - var output = new MemoryStream(); - cs.CopyTo(output); - - return Encoding.UTF8.GetString(output.ToArray()); - } -} \ No newline at end of file diff --git a/Core/Types/AuthorToday/AuthorTodayImage.cs b/Core/Types/AuthorToday/AuthorTodayImage.cs deleted file mode 100644 index e9c16f4b..00000000 --- a/Core/Types/AuthorToday/AuthorTodayImage.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.AuthorToday; - -public class AuthorTodayImage { - [JsonPropertyName("url")] - public string Url { get; set; } -} \ No newline at end of file diff --git a/Core/Types/AuthorToday/AuthorTodayUser.cs b/Core/Types/AuthorToday/AuthorTodayUser.cs deleted file mode 100644 index 9d062ee8..00000000 --- a/Core/Types/AuthorToday/AuthorTodayUser.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.AuthorToday; - -public class AuthorTodayUser { - [JsonPropertyName("id")] - public long Id { get; set; } -} \ No newline at end of file diff --git a/Core/Types/BookYandex/BooksYandexAudioBitrate.cs b/Core/Types/BookYandex/BooksYandexAudioBitrate.cs deleted file mode 100644 index d161f9d7..00000000 --- a/Core/Types/BookYandex/BooksYandexAudioBitrate.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexAudioBitrate { - [JsonPropertyName("url")] - public string Url { get; set; } -} \ No newline at end of file diff --git a/Core/Types/BookYandex/BooksYandexAudioOffline.cs b/Core/Types/BookYandex/BooksYandexAudioOffline.cs deleted file mode 100644 index ffaed28d..00000000 --- a/Core/Types/BookYandex/BooksYandexAudioOffline.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexAudioOffline { - [JsonPropertyName("max_bit_rate")] - public BooksYandexAudioBitrate Max { get; set; } - - [JsonPropertyName("min_bit_rate")] - public BooksYandexAudioBitrate Min { get; set; } -} \ No newline at end of file diff --git a/Core/Types/BookYandex/BooksYandexAudioTrack.cs b/Core/Types/BookYandex/BooksYandexAudioTrack.cs deleted file mode 100644 index 83bcf1cd..00000000 --- a/Core/Types/BookYandex/BooksYandexAudioTrack.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexAudioTrack { - [JsonPropertyName("offline")] - public BooksYandexAudioOffline Offline { get; set; } -} \ No newline at end of file diff --git a/Core/Types/BookYandex/BooksYandexAuthor.cs b/Core/Types/BookYandex/BooksYandexAuthor.cs deleted file mode 100644 index bdd955b1..00000000 --- a/Core/Types/BookYandex/BooksYandexAuthor.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexAuthor { - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("uuid")] - public string Uuid { get; set; } -} \ No newline at end of file diff --git a/Core/Types/BookYandex/BooksYandexBook.cs b/Core/Types/BookYandex/BooksYandexBook.cs deleted file mode 100644 index 3621b97b..00000000 --- a/Core/Types/BookYandex/BooksYandexBook.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Linq; -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public abstract class BookmateBookBase { - [JsonPropertyName("uuid")] - public string UUID { get; set; } - - [JsonPropertyName("annotation")] - public string Annotation { get; set; } - - [JsonPropertyName("cover")] - public BooksYandexCover Cover { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("language")] - public string Language { get; set; } - - [JsonPropertyName("linked_book_uuids")] - public string[] LinkedBooks { get; set; } - - [JsonPropertyName("linked_audiobook_uuids")] - public string[] LinkedAudio { get; set; } - - [JsonPropertyName("source_type")] - public string SourceType { get; set; } - - public abstract BooksYandexAuthor GetAuthor(); -} - -public class BooksYandexBook : BookmateBookBase { - [JsonPropertyName("authors_objects")] - public BooksYandexAuthor[] AuthorsObjects { get; set; } - - public override BooksYandexAuthor GetAuthor() { - return AuthorsObjects?.FirstOrDefault(); - } -} - -public class BookmateComic : BookmateBookBase { - [JsonPropertyName("authors")] - public BooksYandexAuthor[] Authors { get; set; } - - public override BooksYandexAuthor GetAuthor() { - return Authors?.FirstOrDefault(); - } -} \ No newline at end of file diff --git a/Core/Types/BookYandex/BooksYandexComicContent.cs b/Core/Types/BookYandex/BooksYandexComicContent.cs deleted file mode 100644 index 324342a5..00000000 --- a/Core/Types/BookYandex/BooksYandexComicContent.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexComicContent { - [JsonPropertyName("uris")] - public BooksYandexUri Uri { get; set; } -} \ No newline at end of file diff --git a/Core/Types/BookYandex/BooksYandexComicMetadata.cs b/Core/Types/BookYandex/BooksYandexComicMetadata.cs deleted file mode 100644 index fd4c9f8d..00000000 --- a/Core/Types/BookYandex/BooksYandexComicMetadata.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexComicMetadata { - [JsonPropertyName("pages")] - public BooksYandexComicPage[] Pages { get; set; } -} \ No newline at end of file diff --git a/Core/Types/BookYandex/BooksYandexComicPage.cs b/Core/Types/BookYandex/BooksYandexComicPage.cs deleted file mode 100644 index b31ff5a3..00000000 --- a/Core/Types/BookYandex/BooksYandexComicPage.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexComicPage { - [JsonPropertyName("content")] - public BooksYandexComicContent Content { get; set; } -} \ No newline at end of file diff --git a/Core/Types/BookYandex/BooksYandexCover.cs b/Core/Types/BookYandex/BooksYandexCover.cs deleted file mode 100644 index 35f928e0..00000000 --- a/Core/Types/BookYandex/BooksYandexCover.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexCover { - [JsonPropertyName("large")] - public string Large { get; set; } - - [JsonPropertyName("small")] - public string Small { get; set; } -} \ No newline at end of file diff --git a/Core/Types/BookYandex/BooksYandexEpisode.cs b/Core/Types/BookYandex/BooksYandexEpisode.cs deleted file mode 100644 index 4b9b5264..00000000 --- a/Core/Types/BookYandex/BooksYandexEpisode.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexEpisode { - [JsonPropertyName("uuid")] - public string Uuid { get; set; } - - [JsonPropertyName("can_be_read")] - public bool CanBeRead { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } -} \ No newline at end of file diff --git a/Core/Types/BookYandex/BooksYandexEpisodes.cs b/Core/Types/BookYandex/BooksYandexEpisodes.cs deleted file mode 100644 index d153a2d8..00000000 --- a/Core/Types/BookYandex/BooksYandexEpisodes.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexEpisodes { - [JsonPropertyName("episodes")] - public BooksYandexEpisode[] Episodes { get; set; } -} \ No newline at end of file diff --git a/Core/Types/BookYandex/BooksYandexPlaylist.cs b/Core/Types/BookYandex/BooksYandexPlaylist.cs deleted file mode 100644 index 0057ffa1..00000000 --- a/Core/Types/BookYandex/BooksYandexPlaylist.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexPlaylist { - [JsonPropertyName("tracks")] - public BooksYandexAudioTrack[] Tracks { get; set; } -} \ No newline at end of file diff --git a/Core/Types/BookYandex/BooksYandexResponse.cs b/Core/Types/BookYandex/BooksYandexResponse.cs deleted file mode 100644 index 8e9cc5d9..00000000 --- a/Core/Types/BookYandex/BooksYandexResponse.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexResponse { - [JsonPropertyName("book")] - public BooksYandexBook Book { get; set; } - - [JsonPropertyName("audiobook")] - public BooksYandexBook AudioBook { get; set; } - - [JsonPropertyName("comicbook")] - public BookmateComic Comicbook { get; set; } -} \ No newline at end of file diff --git a/Core/Types/BookYandex/BooksYandexUri.cs b/Core/Types/BookYandex/BooksYandexUri.cs deleted file mode 100644 index c8a9e1e0..00000000 --- a/Core/Types/BookYandex/BooksYandexUri.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexUri { - [JsonPropertyName("Image")] - public string Image { get; set; } - - [JsonPropertyName("path")] - public string Path { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Bookriver/BookRiverApiResponse.cs b/Core/Types/Bookriver/BookRiverApiResponse.cs deleted file mode 100644 index 1b1e1274..00000000 --- a/Core/Types/Bookriver/BookRiverApiResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookriver; - -public class BookRiverApiResponse { - [JsonPropertyName("data")] - public T Data { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Bookriver/BookRiverAuthResponse.cs b/Core/Types/Bookriver/BookRiverAuthResponse.cs deleted file mode 100644 index ef18db2c..00000000 --- a/Core/Types/Bookriver/BookRiverAuthResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookriver; - -public class BookRiverAuthResponse { - [JsonPropertyName("token")] - public string Token { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Bookriver/BookRiverBook.cs b/Core/Types/Bookriver/BookRiverBook.cs deleted file mode 100644 index 9ec931f5..00000000 --- a/Core/Types/Bookriver/BookRiverBook.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookriver; - -public class BookRiverBook { - [JsonPropertyName("currentGraphqlBook")] - public BookRiverCurrentBook Book { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Bookriver/BookRiverChapter.cs b/Core/Types/Bookriver/BookRiverChapter.cs deleted file mode 100644 index 6beb9ab4..00000000 --- a/Core/Types/Bookriver/BookRiverChapter.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookriver; - -public class BookRiverChapter { - [JsonPropertyName("id")] - public long Id { get; set; } - - [JsonPropertyName("name")] - public string Name { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Bookriver/BookRiverChapterContent.cs b/Core/Types/Bookriver/BookRiverChapterContent.cs deleted file mode 100644 index 4d08a0fc..00000000 --- a/Core/Types/Bookriver/BookRiverChapterContent.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookriver; - -public class BookRiverChapterContent { - [JsonPropertyName("content")] - public string Content { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Bookriver/BookRiverCurrentBook.cs b/Core/Types/Bookriver/BookRiverCurrentBook.cs deleted file mode 100644 index a648bf81..00000000 --- a/Core/Types/Bookriver/BookRiverCurrentBook.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookriver; - -public class BookRiverCurrentBook { - [JsonPropertyName("id")] - public long Id { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Bookstab/BookstabApiResponse.cs b/Core/Types/Bookstab/BookstabApiResponse.cs deleted file mode 100644 index 33589e81..00000000 --- a/Core/Types/Bookstab/BookstabApiResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookstab; - -public class BookstabApiResponse { - [JsonPropertyName("book")] - public BookstabBook Book { get; set; } - - [JsonPropertyName("chapter")] - public BookstabChapter Chapter { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Bookstab/BookstabBook.cs b/Core/Types/Bookstab/BookstabBook.cs deleted file mode 100644 index 79240ff4..00000000 --- a/Core/Types/Bookstab/BookstabBook.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookstab; - -public class BookstabBook { - [JsonPropertyName("image")] - public string Image { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("excerpt")] - public string Excerpt { get; set; } - - [JsonPropertyName("user")] - public BooksnabUser User { get; set; } - - [JsonPropertyName("chapters_show")] - public BookstabChapter[] ChaptersShow { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Bookstab/BookstabChapter.cs b/Core/Types/Bookstab/BookstabChapter.cs deleted file mode 100644 index 01e10f4a..00000000 --- a/Core/Types/Bookstab/BookstabChapter.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookstab; - -public class BookstabChapter { - [JsonPropertyName("id")] - public int Id { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("body")] - public string Body { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Bookstab/BookstabUser.cs b/Core/Types/Bookstab/BookstabUser.cs deleted file mode 100644 index 6f38050e..00000000 --- a/Core/Types/Bookstab/BookstabUser.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookstab; - -public class BooksnabUser { - [JsonPropertyName("pseudonym")] - public string Pseudonym { get; set; } - - [JsonPropertyName("name")] - public string Name { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Bookstime/BookstimeAuthResponse.cs b/Core/Types/Bookstime/BookstimeAuthResponse.cs deleted file mode 100644 index 3366465e..00000000 --- a/Core/Types/Bookstime/BookstimeAuthResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookstime; - -public class BookstimeAuthResponse { - [JsonPropertyName("X_OCTOBER_ERROR_MESSAGE")] - public string XOctoberErrorMessage { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Boosty/BoostyApiResponse.cs b/Core/Types/Boosty/BoostyApiResponse.cs deleted file mode 100644 index 410418a8..00000000 --- a/Core/Types/Boosty/BoostyApiResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Boosty; - -public class BoostyApiResponse { - [JsonPropertyName("data")] - public T Data { get; set; } - - [JsonPropertyName("extra")] - public BoostyExtra Extra { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Boosty/BoostyExtra.cs b/Core/Types/Boosty/BoostyExtra.cs deleted file mode 100644 index 478980cb..00000000 --- a/Core/Types/Boosty/BoostyExtra.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Boosty; - -public class BoostyExtra { - [JsonPropertyName("isLast")] - public bool IsLast { get; set; } - - [JsonPropertyName("offset")] - public string Offset { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Boosty/BoostyPost.cs b/Core/Types/Boosty/BoostyPost.cs deleted file mode 100644 index b88317e1..00000000 --- a/Core/Types/Boosty/BoostyPost.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace Core.Types.Boosty; - -public class BoostyPost { - [JsonPropertyName("id")] - public Guid Id { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("hasAccess")] - public bool HasAccess { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Common/IdChapter.cs b/Core/Types/Common/IdChapter.cs deleted file mode 100644 index 1d8e774e..00000000 --- a/Core/Types/Common/IdChapter.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Core.Types.Common; - -public record IdChapter(string Id, string Title); \ No newline at end of file diff --git a/Core/Types/Common/UrlChapter.cs b/Core/Types/Common/UrlChapter.cs deleted file mode 100644 index 30577a03..00000000 --- a/Core/Types/Common/UrlChapter.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System; - -namespace Core.Types.Common; - -public record UrlChapter(Uri Url, string Title); \ No newline at end of file diff --git a/Core/Types/DarkNovels/DarkNovelsAuthData.cs b/Core/Types/DarkNovels/DarkNovelsAuthData.cs deleted file mode 100644 index 7ebdca90..00000000 --- a/Core/Types/DarkNovels/DarkNovelsAuthData.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.DarkNovels; - -public class DarkNovelsAuthResponse { - [JsonPropertyName("token")] - public DarkNovelsAuthToken Token { get; set; } -} \ No newline at end of file diff --git a/Core/Types/DarkNovels/DarkNovelsAuthToken.cs b/Core/Types/DarkNovels/DarkNovelsAuthToken.cs deleted file mode 100644 index b573227d..00000000 --- a/Core/Types/DarkNovels/DarkNovelsAuthToken.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.DarkNovels; - -public class DarkNovelsAuthToken { - [JsonPropertyName("accessToken")] - public string AccessToken { get; set; } -} \ No newline at end of file diff --git a/Core/Types/DarkNovels/DarkNovelsChapter.cs b/Core/Types/DarkNovels/DarkNovelsChapter.cs deleted file mode 100644 index abf2df1e..00000000 --- a/Core/Types/DarkNovels/DarkNovelsChapter.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.DarkNovels; - -public class DarkNovelsChapter { - [JsonPropertyName("id")] - public int Id { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("payed")] - public int Payed { get; set; } -} \ No newline at end of file diff --git a/Core/Types/DarkNovels/DarkNovelsData.cs b/Core/Types/DarkNovels/DarkNovelsData.cs deleted file mode 100644 index 0e118a65..00000000 --- a/Core/Types/DarkNovels/DarkNovelsData.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.DarkNovels; - -public class DarkNovelsData { - [JsonPropertyName("status")] - public string Status { get; set; } - - [JsonPropertyName("message")] - public string Message { get; set; } - - [JsonPropertyName("data")] - public T Data { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Dreame/DreameApiResponse.cs b/Core/Types/Dreame/DreameApiResponse.cs deleted file mode 100644 index e2d5803b..00000000 --- a/Core/Types/Dreame/DreameApiResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Dreame; - -public class DreameApiResponse { - [JsonPropertyName("data")] - public T Data { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Dreame/DreameCatalog.cs b/Core/Types/Dreame/DreameCatalog.cs deleted file mode 100644 index 523a2541..00000000 --- a/Core/Types/Dreame/DreameCatalog.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Dreame; - -public class DreameCatalog { - [JsonPropertyName("pager")] - public DreamePager Pager { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Dreame/DreameChapter.cs b/Core/Types/Dreame/DreameChapter.cs deleted file mode 100644 index a052ac89..00000000 --- a/Core/Types/Dreame/DreameChapter.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Dreame; - -public class DreameChapter { - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("id")] - public string Id { get; set; } - - [JsonPropertyName("content")] - public string Content { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Dreame/DreameNovel.cs b/Core/Types/Dreame/DreameNovel.cs deleted file mode 100644 index c57d2625..00000000 --- a/Core/Types/Dreame/DreameNovel.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Dreame; - -public class DreameNovel { - [JsonPropertyName("id")] - public long Id { get; set; } - - [JsonPropertyName("author_id")] - public string AuthorId { get; set; } - - [JsonPropertyName("author_name")] - public string AuthorName { get; set; } - - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("descr")] - public string Description { get; set; } - - [JsonPropertyName("language")] - public string Language { get; set; } - - [JsonPropertyName("cover_url")] - public string Cover { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Dreame/DreamePager.cs b/Core/Types/Dreame/DreamePager.cs deleted file mode 100644 index 26ed24a2..00000000 --- a/Core/Types/Dreame/DreamePager.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Core.Types.Dreame; - -public class DreamePager { - [JsonPropertyName("chap_list")] - public List ChapterList { get; set; } -} \ No newline at end of file diff --git a/Core/Types/FanFicus/FanFicusApiResponse.cs b/Core/Types/FanFicus/FanFicusApiResponse.cs deleted file mode 100644 index e83bf349..00000000 --- a/Core/Types/FanFicus/FanFicusApiResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.FanFicus; - -public class FanFicusApiResponse { - [JsonPropertyName("value")] - public T Value { get; set; } -} \ No newline at end of file diff --git a/Core/Types/FanFicus/FanFicusBook.cs b/Core/Types/FanFicus/FanFicusBook.cs deleted file mode 100644 index fc8d9aa9..00000000 --- a/Core/Types/FanFicus/FanFicusBook.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.FanFicus; - -public class FanFicusBook { - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("description")] - public string Description { get; set; } - - [JsonPropertyName("creatorId")] - public FanFicusCreator[] Creators { get; set; } - - [JsonPropertyName("images")] - public FanFicusImage[] Images { get; set; } -} \ No newline at end of file diff --git a/Core/Types/FanFicus/FanFicusChapter.cs b/Core/Types/FanFicus/FanFicusChapter.cs deleted file mode 100644 index d071ac34..00000000 --- a/Core/Types/FanFicus/FanFicusChapter.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.FanFicus; - -public class FanFicusChapter { - [JsonPropertyName("text")] - public string Text { get; set; } -} \ No newline at end of file diff --git a/Core/Types/FanFicus/FanFicusCreator.cs b/Core/Types/FanFicus/FanFicusCreator.cs deleted file mode 100644 index dc46acfc..00000000 --- a/Core/Types/FanFicus/FanFicusCreator.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.FanFicus; - -public class FanFicusCreator { - [JsonPropertyName("_id")] - public string Id { get; set; } - - [JsonPropertyName("nickName")] - public string NickName { get; set; } -} \ No newline at end of file diff --git a/Core/Types/FanFicus/FanFicusImage.cs b/Core/Types/FanFicus/FanFicusImage.cs deleted file mode 100644 index 096d1f5c..00000000 --- a/Core/Types/FanFicus/FanFicusImage.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.FanFicus; - -public class FanFicusImage { - [JsonPropertyName("url")] - public string Url { get; set; } -} \ No newline at end of file diff --git a/Core/Types/FanFicus/FanFicusPart.cs b/Core/Types/FanFicus/FanFicusPart.cs deleted file mode 100644 index eb4cf839..00000000 --- a/Core/Types/FanFicus/FanFicusPart.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.FanFicus; - -public class FanFicusPart { - [JsonPropertyName("_id")] - public string Id { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } -} \ No newline at end of file diff --git a/Core/Types/FanFicus/FanFicusUser.cs b/Core/Types/FanFicus/FanFicusUser.cs deleted file mode 100644 index 2491d58c..00000000 --- a/Core/Types/FanFicus/FanFicusUser.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.FanFicus; - -public class FanFicusUser { - [JsonPropertyName("token")] - public string Token { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Freedlit/FreedlitApiResponse.cs b/Core/Types/Freedlit/FreedlitApiResponse.cs deleted file mode 100644 index 6ef791ac..00000000 --- a/Core/Types/Freedlit/FreedlitApiResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitApiResponse { - [JsonPropertyName("success")] - public T Success { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Freedlit/FreedlitApp.cs b/Core/Types/Freedlit/FreedlitApp.cs deleted file mode 100644 index d698b42b..00000000 --- a/Core/Types/Freedlit/FreedlitApp.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitApp { - [JsonPropertyName("props")] - public FreedlitProps Props { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Freedlit/FreedlitAuthError.cs b/Core/Types/Freedlit/FreedlitAuthError.cs deleted file mode 100644 index 9685a0ee..00000000 --- a/Core/Types/Freedlit/FreedlitAuthError.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitAuthError { - [JsonPropertyName("email")] - public string Email { get; set; } - - [JsonPropertyName("password")] - public string[] Password { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Freedlit/FreedlitAuthor.cs b/Core/Types/Freedlit/FreedlitAuthor.cs deleted file mode 100644 index 436245a8..00000000 --- a/Core/Types/Freedlit/FreedlitAuthor.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitAuthor { - [JsonPropertyName("id")] - public long Id { get; set; } - - [JsonPropertyName("user_link")] - public string UserLink { get; set; } - - [JsonPropertyName("name")] - public string Name { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Freedlit/FreedlitBook.cs b/Core/Types/Freedlit/FreedlitBook.cs deleted file mode 100644 index 9debc8f7..00000000 --- a/Core/Types/Freedlit/FreedlitBook.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitBook { - [JsonPropertyName("id")] - public long Id { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("cover")] - public string Cover { get; set; } - - [JsonPropertyName("annotation")] - public string Annotation { get; set; } - - [JsonPropertyName("language")] - public string Language { get; set; } - - [JsonPropertyName("main_author")] - public FreedlitAuthor MainAuthor { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Freedlit/FreedlitChapter.cs b/Core/Types/Freedlit/FreedlitChapter.cs deleted file mode 100644 index bd5dfb98..00000000 --- a/Core/Types/Freedlit/FreedlitChapter.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitChapter { - [JsonPropertyName("id")] - public long Id { get; set; } - - [JsonPropertyName("header")] - public string Header { get; set; } - - [JsonPropertyName("content")] - public string Content { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Freedlit/FreedlitItemsContent.cs b/Core/Types/Freedlit/FreedlitItemsContent.cs deleted file mode 100644 index 29249e6b..00000000 --- a/Core/Types/Freedlit/FreedlitItemsContent.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitItemsContent { - [JsonPropertyName("items")] - public List Items { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Freedlit/FreedlitProps.cs b/Core/Types/Freedlit/FreedlitProps.cs deleted file mode 100644 index 6f8ca76e..00000000 --- a/Core/Types/Freedlit/FreedlitProps.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitProps { - [JsonPropertyName("book")] - public FreedlitBook Book { get; set; } - - [JsonPropertyName("errors")] - public TErr Errors { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Freedlit/FreedlitSuccessResponse.cs b/Core/Types/Freedlit/FreedlitSuccessResponse.cs deleted file mode 100644 index 14944614..00000000 --- a/Core/Types/Freedlit/FreedlitSuccessResponse.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitSuccessResponse { - [JsonPropertyName("items")] - public List Items { get; set; } -} \ No newline at end of file diff --git a/Core/Types/HotNovelPub/HotNovelPubApiResponse.cs b/Core/Types/HotNovelPub/HotNovelPubApiResponse.cs deleted file mode 100644 index 55cca7ab..00000000 --- a/Core/Types/HotNovelPub/HotNovelPubApiResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.HotNovelPub; - -public class HotNovelPubApiResponse { - [JsonPropertyName("data")] - public T Data { get; set; } -} \ No newline at end of file diff --git a/Core/Types/HotNovelPub/HotNovelPubAuthorize.cs b/Core/Types/HotNovelPub/HotNovelPubAuthorize.cs deleted file mode 100644 index 219a9faf..00000000 --- a/Core/Types/HotNovelPub/HotNovelPubAuthorize.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.HotNovelPub; - -public class HotNovelPubAuthorize { - [JsonPropertyName("id")] - public int Id { get; set; } - - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("slug")] - public string Slug { get; set; } -} \ No newline at end of file diff --git a/Core/Types/HotNovelPub/HotNovelPubBook.cs b/Core/Types/HotNovelPub/HotNovelPubBook.cs deleted file mode 100644 index 76c3682f..00000000 --- a/Core/Types/HotNovelPub/HotNovelPubBook.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.HotNovelPub; - -public class HotNovelPubBook { - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("description")] - public string Description { get; set; } - - [JsonPropertyName("authorize")] - public HotNovelPubAuthorize Authorize { get; set; } - - [JsonPropertyName("image")] - public string Image { get; set; } -} \ No newline at end of file diff --git a/Core/Types/HotNovelPub/HotNovelPubBookResponse.cs b/Core/Types/HotNovelPub/HotNovelPubBookResponse.cs deleted file mode 100644 index 421ac832..00000000 --- a/Core/Types/HotNovelPub/HotNovelPubBookResponse.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Core.Types.HotNovelPub; - -public class HotNovelPubBookResponse { - [JsonPropertyName("book")] - public HotNovelPubBook Book { get; set; } - - [JsonPropertyName("chapters")] - public List Chapters { get; set; } -} \ No newline at end of file diff --git a/Core/Types/HotNovelPub/HotNovelPubChapter.cs b/Core/Types/HotNovelPub/HotNovelPubChapter.cs deleted file mode 100644 index 15739fc7..00000000 --- a/Core/Types/HotNovelPub/HotNovelPubChapter.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.HotNovelPub; - -public class HotNovelPubChapter { - [JsonPropertyName("id")] - public int Id { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("slug")] - public string Slug { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Librebook/LibrebookAuthResponse.cs b/Core/Types/Librebook/LibrebookAuthResponse.cs deleted file mode 100644 index c4269177..00000000 --- a/Core/Types/Librebook/LibrebookAuthResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Librebook; - -public class LibrebookAuthResponse { - [JsonPropertyName("type")] - public string Type { get; set; } - - [JsonPropertyName("text")] - public string Text { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litexit/LitexitChapter.cs b/Core/Types/Litexit/LitexitChapter.cs deleted file mode 100644 index c8a0c325..00000000 --- a/Core/Types/Litexit/LitexitChapter.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litexit; - -public class LitexitChapter { - [JsonPropertyName("id")] - public int Id { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("text")] - public string Text { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litexit/LitexitUser.cs b/Core/Types/Litexit/LitexitUser.cs deleted file mode 100644 index a1a08bf0..00000000 --- a/Core/Types/Litexit/LitexitUser.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litexit; - -public class LitexitUser { - [JsonPropertyName("id")] - public int Id { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litgorod/LitgorodAuthResponse.cs b/Core/Types/Litgorod/LitgorodAuthResponse.cs deleted file mode 100644 index 916b0cbc..00000000 --- a/Core/Types/Litgorod/LitgorodAuthResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litgorod; - -public class LitgorodAuthResponse { - [JsonPropertyName("message")] - public string Message { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litgorod/LitgorodBookPart.cs b/Core/Types/Litgorod/LitgorodBookPart.cs deleted file mode 100644 index 748fb176..00000000 --- a/Core/Types/Litgorod/LitgorodBookPart.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litgorod; - -public class LitgorodBookPart { - [JsonPropertyName("text")] - public string Text { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litgorod/LitgorodChapter.cs b/Core/Types/Litgorod/LitgorodChapter.cs deleted file mode 100644 index 2cf935c0..00000000 --- a/Core/Types/Litgorod/LitgorodChapter.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litgorod; - -public class LitgorodChapter { - [JsonPropertyName("explodedParagraphs")] - public string[] Paragraphs { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litmarket/AuthResponse.cs b/Core/Types/Litmarket/AuthResponse.cs deleted file mode 100644 index f3d073aa..00000000 --- a/Core/Types/Litmarket/AuthResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litmarket; - -public class AuthResponse { - [JsonPropertyName("success")] - public bool Success { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litmarket/Block.cs b/Core/Types/Litmarket/Block.cs deleted file mode 100644 index 4fd6f666..00000000 --- a/Core/Types/Litmarket/Block.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litmarket; - -public class Block { - [JsonPropertyName("chunk")] - public Chunk Chunk { get; set; } - - [JsonPropertyName("index")] - public int Index { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litmarket/Chunk.cs b/Core/Types/Litmarket/Chunk.cs deleted file mode 100644 index 1ce3645a..00000000 --- a/Core/Types/Litmarket/Chunk.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litmarket; - -public class Chunk { - [JsonPropertyName("mods")] - public Mod[] Mods { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litmarket/LBook.cs b/Core/Types/Litmarket/LBook.cs deleted file mode 100644 index 0112f07c..00000000 --- a/Core/Types/Litmarket/LBook.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litmarket; - -public class LBook { - [JsonPropertyName("ebookId")] - public int EbookId { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litmarket/Mod.cs b/Core/Types/Litmarket/Mod.cs deleted file mode 100644 index 84a5d596..00000000 --- a/Core/Types/Litmarket/Mod.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Core.Types.Litmarket; - -public class Mod { - [JsonPropertyName("type")] - public string Type { get; set; } - - [JsonPropertyName("styles")] - public string[] Styles { get; set; } - - [JsonPropertyName("text")] - public string Text { get; set; } - - [JsonPropertyName("data")] - public JsonElement Data { get; set; } - - [JsonPropertyName("mods")] - public Mod[] Mods { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litmarket/Response.cs b/Core/Types/Litmarket/Response.cs deleted file mode 100644 index 52a8d3f2..00000000 --- a/Core/Types/Litmarket/Response.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litmarket; - -public class Response { - [JsonPropertyName("book")] - public LBook Book { get; set; } - - [JsonPropertyName("tableOfContent")] - public string Toc { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litnet/LitnetAuthResponse.cs b/Core/Types/Litnet/LitnetAuthResponse.cs deleted file mode 100644 index e8dc4483..00000000 --- a/Core/Types/Litnet/LitnetAuthResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litnet; - -public class LitnetAuthResponse { - [JsonPropertyName("error")] - public string Error { get; set; } - - [JsonPropertyName("token")] - public string Token { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litnet/LitnetBookResponse.cs b/Core/Types/Litnet/LitnetBookResponse.cs deleted file mode 100644 index eb330bef..00000000 --- a/Core/Types/Litnet/LitnetBookResponse.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litnet; - -public class LitnetBookResponse { - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("author_name")] - public string AuthorName { get; set; } - - [JsonPropertyName("cover")] - public string Cover { get; set; } - - [JsonPropertyName("annotation")] - public string Annotation { get; set; } - - [JsonPropertyName("url")] - public string Url { get; set; } - - [JsonPropertyName("author_id")] - public long AuthorId { get; set; } - - [JsonPropertyName("lang")] - public string Lang { get; set; } - - [JsonPropertyName("cycle_priority")] - public int? CyclePriority { get; set; } - - [JsonPropertyName("adult_only")] - public bool AdultOnly { get; set; } - - [JsonPropertyName("co_author_name")] - public string CoAuthorName { get; set; } - - [JsonPropertyName("co_author")] - public long? CoAuthorId { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litnet/LitnetChapterResponse.cs b/Core/Types/Litnet/LitnetChapterResponse.cs deleted file mode 100644 index b81f3a04..00000000 --- a/Core/Types/Litnet/LitnetChapterResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litnet; - -public class LitnetChapterResponse { - [JsonPropertyName("id")] - public long Id { get; set; } - - [JsonPropertyName("text")] - public string Text { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litnet/LitnetContentsResponse.cs b/Core/Types/Litnet/LitnetContentsResponse.cs deleted file mode 100644 index 37b2d269..00000000 --- a/Core/Types/Litnet/LitnetContentsResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litnet; - -public class LitnetContentsResponse { - [JsonPropertyName("id")] - public long Id { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litres/LitresPayload.cs b/Core/Types/Litres/LitresPayload.cs deleted file mode 100644 index 6b87383b..00000000 --- a/Core/Types/Litres/LitresPayload.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Text.Json.Serialization; - -namespace Core.Types.Litres; - -public class LitresPayload { - [JsonPropertyName("sha")] - public string Sha { get; set; } - - [JsonPropertyName("sid")] - public string Sid { get; set; } - - [JsonPropertyName("uilang")] - public string UiLang { get; set; } = "rus"; - - [JsonPropertyName("time")] - public string Time { get; set; } - - [JsonPropertyName("mobile_app")] - public string App { get; set; } - - [JsonPropertyName("requests")] - public List Requests { get; set; } = new(); - - public static LitresPayload Create(DateTime date, string sid, string secret, string app) { - var result = new LitresPayload { - Time = date.ToString("yyyy-MM-ddTHH:mm:sszzzz"), - App = app, - Sid = sid - }; - - result.Sha = Sha256(result.Time + secret); - - return result; - } - - private static string Sha256(string str) { - var crypt = SHA256.Create(); - var crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(str), 0, Encoding.UTF8.GetByteCount(str)); - - return crypto.Aggregate(new StringBuilder(), (sb, b) => sb.Append(b.ToString("x2"))).ToString(); - } -} \ No newline at end of file diff --git a/Core/Types/Litres/Requests/LitresAuthData.cs b/Core/Types/Litres/Requests/LitresAuthData.cs deleted file mode 100644 index 24b96eeb..00000000 --- a/Core/Types/Litres/Requests/LitresAuthData.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Requests; - -public class LitresAuthData { - [JsonPropertyName("login")] - public string Login { get; set; } - - [JsonPropertyName("pwd")] - public string Password { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litres/Requests/LitresBrowseArtsData.cs b/Core/Types/Litres/Requests/LitresBrowseArtsData.cs deleted file mode 100644 index 49fe6d72..00000000 --- a/Core/Types/Litres/Requests/LitresBrowseArtsData.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Requests; - -public class LitresBrowseArtsData { - [JsonPropertyName("currency")] - public string Currency { get; set; } - - [JsonPropertyName("anno")] - public string Anno { get; set; } - - [JsonPropertyName("id")] - public string[] Id { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litres/Requests/LitresRequestBase.cs b/Core/Types/Litres/Requests/LitresRequestBase.cs deleted file mode 100644 index fe157b38..00000000 --- a/Core/Types/Litres/Requests/LitresRequestBase.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Requests; - -public abstract class LitresRequestBase where T : new() { - [JsonPropertyName("id")] - public string Id { get; set; } = "data"; - - [JsonPropertyName("func")] - public string Func { get; set; } - - [JsonPropertyName("param")] - public T Param { get; } = new(); -} \ No newline at end of file diff --git a/Core/Types/Litres/Response/LitresArt.cs b/Core/Types/Litres/Response/LitresArt.cs deleted file mode 100644 index fec94f86..00000000 --- a/Core/Types/Litres/Response/LitresArt.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresArt { - [JsonPropertyName("id")] - public long Id { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("html_annotation")] - public string Annotation { get; set; } - - [JsonPropertyName("persons")] - public LitresPerson[] Persons { get; set; } = []; - - [JsonPropertyName("cover_url")] - public string Cover { get; set; } - - [JsonPropertyName("series")] - public LitresSeria[] Sequences { get; set; } = []; - - [JsonPropertyName("linked_arts")] - public LitresArt[] LinkedArts { get; set; } - - [JsonPropertyName("art_type")] - public LitresArtTypeEnum ArtType { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litres/Response/LitresAuthResponseData.cs b/Core/Types/Litres/Response/LitresAuthResponseData.cs deleted file mode 100644 index c6f212a1..00000000 --- a/Core/Types/Litres/Response/LitresAuthResponseData.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresAuthResponseData { - [JsonPropertyName("success")] - public bool Success { get; set; } - - [JsonPropertyName("sid")] - public string Sid { get; set; } - - [JsonPropertyName("error_message")] - public string ErrorMessage { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litres/Response/LitresFile.cs b/Core/Types/Litres/Response/LitresFile.cs deleted file mode 100644 index 981a68c2..00000000 --- a/Core/Types/Litres/Response/LitresFile.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresFile { - [JsonPropertyName("extension")] - public string Extension { get; set; } - - [JsonPropertyName("id")] - public long Id { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litres/Response/LitresFiles.cs b/Core/Types/Litres/Response/LitresFiles.cs deleted file mode 100644 index 703c82fc..00000000 --- a/Core/Types/Litres/Response/LitresFiles.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresFiles { - [JsonPropertyName("files")] - public LitresFile[] Files { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litres/Response/LitresMe.cs b/Core/Types/Litres/Response/LitresMe.cs deleted file mode 100644 index d4379d42..00000000 --- a/Core/Types/Litres/Response/LitresMe.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresMe { - [JsonPropertyName("partner_subscriptions")] - public LitresPartnerSubscriptions PartnerSubscriptions { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litres/Response/LitresPartnerSubscriptions.cs b/Core/Types/Litres/Response/LitresPartnerSubscriptions.cs deleted file mode 100644 index b959fbb3..00000000 --- a/Core/Types/Litres/Response/LitresPartnerSubscriptions.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresPartnerSubscriptions { - [JsonPropertyName("subscriptions")] - public LitresSubscription[] Subscriptions { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litres/Response/LitresPerson.cs b/Core/Types/Litres/Response/LitresPerson.cs deleted file mode 100644 index 87d69808..00000000 --- a/Core/Types/Litres/Response/LitresPerson.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresPerson { - [JsonPropertyName("id")] - public TId Id { get; set; } - - [JsonPropertyName("full_name")] - public string FullName { get; set; } - - [JsonPropertyName("role")] - public string Role { get; set; } - - [JsonPropertyName("url")] - public string Url { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litres/Response/LitresResponse.cs b/Core/Types/Litres/Response/LitresResponse.cs deleted file mode 100644 index 91f222a0..00000000 --- a/Core/Types/Litres/Response/LitresResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresResponse { - [JsonPropertyName("data")] - public T Data { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litres/Response/LitresSeria.cs b/Core/Types/Litres/Response/LitresSeria.cs deleted file mode 100644 index 948d61ae..00000000 --- a/Core/Types/Litres/Response/LitresSeria.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresSeria { - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("id")] - public long Id { get; set; } - - [JsonPropertyName("sequence_number")] - public string SequenceNumber { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litres/Response/LitresStaticResponse.cs b/Core/Types/Litres/Response/LitresStaticResponse.cs deleted file mode 100644 index 0a5d2038..00000000 --- a/Core/Types/Litres/Response/LitresStaticResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresStaticResponse { - [JsonPropertyName("payload")] - public LitresResponse Payload { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litres/Response/LitresSubscription.cs b/Core/Types/Litres/Response/LitresSubscription.cs deleted file mode 100644 index f662f9d0..00000000 --- a/Core/Types/Litres/Response/LitresSubscription.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresSubscription { - [JsonPropertyName("is_active")] - public bool IsActive { get; set; } - - [JsonPropertyName("host")] - public string Host { get; set; } - - [JsonPropertyName("type")] - public string Type { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Litsovet/LitsovetLoginResponse.cs b/Core/Types/Litsovet/LitsovetLoginResponse.cs deleted file mode 100644 index a9a61917..00000000 --- a/Core/Types/Litsovet/LitsovetLoginResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litsovet; - -public class LitsovetLoginResponse { - [JsonPropertyName("ok")] - public int Ok { get; set; } -} \ No newline at end of file diff --git a/Core/Types/MangaLib/MangaLibPg.cs b/Core/Types/MangaLib/MangaLibPg.cs deleted file mode 100644 index 66c5f9fd..00000000 --- a/Core/Types/MangaLib/MangaLibPg.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.MangaLib; - -public class MangaLibPg { - [JsonPropertyName("p")] - public int P { get; set; } - - [JsonPropertyName("u")] - public string U { get; set; } -} \ No newline at end of file diff --git a/Core/Types/MyBook/MyBookAuth.cs b/Core/Types/MyBook/MyBookAuth.cs deleted file mode 100644 index 8dc38bdb..00000000 --- a/Core/Types/MyBook/MyBookAuth.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.MyBook; - -public class MyBookAuth { - [JsonPropertyName("token")] - public string Token { get; set; } - - [JsonPropertyName("secret")] - public string Secret { get; set; } -} \ No newline at end of file diff --git a/Core/Types/MyBook/MyBookAuthor.cs b/Core/Types/MyBook/MyBookAuthor.cs deleted file mode 100644 index 0f1abdd1..00000000 --- a/Core/Types/MyBook/MyBookAuthor.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.MyBook; - -public class MyBookAuthor { - [JsonPropertyName("cover_name")] - public string Name { get; set; } - - [JsonPropertyName("absolute_url")] - public string Url { get; set; } -} \ No newline at end of file diff --git a/Core/Types/MyBook/MyBookBook.cs b/Core/Types/MyBook/MyBookBook.cs deleted file mode 100644 index 5304e2fd..00000000 --- a/Core/Types/MyBook/MyBookBook.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -namespace Core.Types.MyBook; - -public class MyBookBook { - [JsonPropertyName("id")] - public long Id { get; set; } - - [JsonPropertyName("annotation")] - public string Annotation { get; set; } - - [JsonPropertyName("bookfile")] - public string BookFile { get; set; } - - [JsonPropertyName("default_cover")] - public string Cover { get; set; } - - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("authors")] - public MyBookAuthor[] Authors { get; set; } - - [JsonPropertyName("type")] - public string Type { get; set; } - - [JsonPropertyName("mapfiles")] - public MyBookMapFile[] MapFiles { get; set; } - - [JsonPropertyName("connected_book")] - public MyBookBook Connected { get; set; } - - [JsonPropertyName("bookfiles")] - public JsonNode[] Files { get; set; } -} \ No newline at end of file diff --git a/Core/Types/MyBook/MyBookFile.cs b/Core/Types/MyBook/MyBookFile.cs deleted file mode 100644 index e6ed4f9d..00000000 --- a/Core/Types/MyBook/MyBookFile.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.MyBook; - -public class MyBookFile { - [JsonPropertyName("url")] - public string Url { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("order")] - public int Order { get; set; } -} \ No newline at end of file diff --git a/Core/Types/MyBook/MyBookMapFile.cs b/Core/Types/MyBook/MyBookMapFile.cs deleted file mode 100644 index 6929b795..00000000 --- a/Core/Types/MyBook/MyBookMapFile.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.MyBook; - -public class MyBookMapFile { - [JsonPropertyName("book")] - public long Book { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Neobook/NeobookAttachment.cs b/Core/Types/Neobook/NeobookAttachment.cs deleted file mode 100644 index bc3be366..00000000 --- a/Core/Types/Neobook/NeobookAttachment.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookAttachment { - [JsonPropertyName("image")] - public Dictionary Cover { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Neobook/NeobookAuth.cs b/Core/Types/Neobook/NeobookAuth.cs deleted file mode 100644 index a80c5ce0..00000000 --- a/Core/Types/Neobook/NeobookAuth.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookAuth { - [JsonPropertyName("error")] - public string Error { get; set; } - - [JsonPropertyName("login")] - public NeobookLogin Login { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Neobook/NeobookBook.cs b/Core/Types/Neobook/NeobookBook.cs deleted file mode 100644 index 258124e5..00000000 --- a/Core/Types/Neobook/NeobookBook.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookBook { - [JsonPropertyName("chapters")] - public NeobookChapter[] Chapters { get; set; } - - [JsonPropertyName("active_chapter_index")] - public int ActiveChapterIndex { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Neobook/NeobookChapter.cs b/Core/Types/Neobook/NeobookChapter.cs deleted file mode 100644 index 5123e58a..00000000 --- a/Core/Types/Neobook/NeobookChapter.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookChapter { - [JsonPropertyName("data")] - public NeobookChapterData Data { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Neobook/NeobookChapterData.cs b/Core/Types/Neobook/NeobookChapterData.cs deleted file mode 100644 index d31bb149..00000000 --- a/Core/Types/Neobook/NeobookChapterData.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookChapterData { - [JsonPropertyName("html")] - public string Html { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Neobook/NeobookLogin.cs b/Core/Types/Neobook/NeobookLogin.cs deleted file mode 100644 index 14d612f1..00000000 --- a/Core/Types/Neobook/NeobookLogin.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookLogin { - [JsonPropertyName("utoken")] - public string Utoken { get; set; } - - [JsonPropertyName("uid")] - public string Uid { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Neobook/NeobookPostData.cs b/Core/Types/Neobook/NeobookPostData.cs deleted file mode 100644 index 9fc029f3..00000000 --- a/Core/Types/Neobook/NeobookPostData.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookPostData { - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("text")] - public string Text { get; set; } - - [JsonPropertyName("user")] - public NeobookUser User { get; set; } - - [JsonPropertyName("chapters")] - public NeobookTocChapter[] Chapters { get; set; } - - [JsonPropertyName("attachment")] - public NeobookAttachment Attachment { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Neobook/NeobookTocChapter.cs b/Core/Types/Neobook/NeobookTocChapter.cs deleted file mode 100644 index 1e3a8ae8..00000000 --- a/Core/Types/Neobook/NeobookTocChapter.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookTocChapter { - [JsonPropertyName("token")] - public string Token { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Neobook/NeobookUser.cs b/Core/Types/Neobook/NeobookUser.cs deleted file mode 100644 index 15084228..00000000 --- a/Core/Types/Neobook/NeobookUser.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookUser { - [JsonPropertyName("username")] - public string UserName { get; set; } - - [JsonPropertyName("firstname")] - public string FirstName { get; set; } - - [JsonPropertyName("lastname")] - public string LastName { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Ranobe/RanobeBook.cs b/Core/Types/Ranobe/RanobeBook.cs deleted file mode 100644 index 2a282490..00000000 --- a/Core/Types/Ranobe/RanobeBook.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Core.Types.Ranobe; - -public class RanobeBook { - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("author")] - public string Author { get; set; } - - [JsonPropertyName("verticalImages")] - public RanobeImage[] Images { get; set; } - - [JsonPropertyName("verticalImage")] - public RanobeImage Image { get; set; } - - [JsonPropertyName("chapters")] - public List Chapters { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Ranobe/RanobeChapter.cs b/Core/Types/Ranobe/RanobeChapter.cs deleted file mode 100644 index 45622675..00000000 --- a/Core/Types/Ranobe/RanobeChapter.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Ranobe; - -public class RanobeChapter { - [JsonPropertyName("content")] - public RanobeChapterContent Content { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Ranobe/RanobeChapterContent.cs b/Core/Types/Ranobe/RanobeChapterContent.cs deleted file mode 100644 index 6ed1465f..00000000 --- a/Core/Types/Ranobe/RanobeChapterContent.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Ranobe; - -public class RanobeChapterContent { - [JsonPropertyName("text")] - public string Text { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Ranobe/RanobeChapterShort.cs b/Core/Types/Ranobe/RanobeChapterShort.cs deleted file mode 100644 index 0e6e650a..00000000 --- a/Core/Types/Ranobe/RanobeChapterShort.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Ranobe; - -public class RanobeChapterShort { - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("url")] - public string Url { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Ranobe/RanobeImage.cs b/Core/Types/Ranobe/RanobeImage.cs deleted file mode 100644 index 86918b93..00000000 --- a/Core/Types/Ranobe/RanobeImage.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Ranobe; - -public class RanobeImage { - [JsonPropertyName("height")] - public int Height { get; set; } - - [JsonPropertyName("url")] - public string Url { get; set; } -} \ No newline at end of file diff --git a/Core/Types/RanobeHub/RanobeHubApiResponse.cs b/Core/Types/RanobeHub/RanobeHubApiResponse.cs deleted file mode 100644 index 9bae700e..00000000 --- a/Core/Types/RanobeHub/RanobeHubApiResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeHub; - -public class RanobeHubApiResponse { - [JsonPropertyName("volumes")] - public RanobeHubVolume[] Volumes { get; set; } -} \ No newline at end of file diff --git a/Core/Types/RanobeHub/RanobeHubChapter.cs b/Core/Types/RanobeHub/RanobeHubChapter.cs deleted file mode 100644 index f173eb87..00000000 --- a/Core/Types/RanobeHub/RanobeHubChapter.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeHub; - -public class RanobeHubChapter { - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("url")] - public string Url { get; set; } -} \ No newline at end of file diff --git a/Core/Types/RanobeHub/RanobeHubVolume.cs b/Core/Types/RanobeHub/RanobeHubVolume.cs deleted file mode 100644 index 3f73b73d..00000000 --- a/Core/Types/RanobeHub/RanobeHubVolume.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeHub; - -public class RanobeHubVolume { - [JsonPropertyName("chapters")] - public RanobeHubChapter[] Chapters { get; set; } -} \ No newline at end of file diff --git a/Core/Types/RanobeNovels/RanobeNovelsChapter.cs b/Core/Types/RanobeNovels/RanobeNovelsChapter.cs deleted file mode 100644 index 4a3f32c0..00000000 --- a/Core/Types/RanobeNovels/RanobeNovelsChapter.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeNovels; - -public class RanobeNovelsChapter { - [JsonPropertyName("post_name")] - public string Name { get; set; } - - [JsonPropertyName("post_title")] - public string Title { get; set; } -} \ No newline at end of file diff --git a/Core/Types/RanobeOvh/RanobeOvhBranch.cs b/Core/Types/RanobeOvh/RanobeOvhBranch.cs deleted file mode 100644 index 2e5293fc..00000000 --- a/Core/Types/RanobeOvh/RanobeOvhBranch.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeOvh; - -public class RanobeOvhBranch { - [JsonPropertyName("id")] - public string Id { get; set; } - - [JsonPropertyName("translators")] - public RanoveOvhTranslator[] Translators { get; set; } - - [JsonPropertyName("chaptersCount")] - public long ChaptersCount { get; set; } -} \ No newline at end of file diff --git a/Core/Types/RanobeOvh/RanobeOvhChapterFull.cs b/Core/Types/RanobeOvh/RanobeOvhChapterFull.cs deleted file mode 100644 index b9090b69..00000000 --- a/Core/Types/RanobeOvh/RanobeOvhChapterFull.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeOvh; - -public class RanobeOvhChapterFull { - [JsonPropertyName("pages")] - public RanobeOvhPage[] Pages { get; set; } - - [JsonPropertyName("content")] - public JsonNode Content { get; set; } -} - - -public class RanobeOvhChapterShort { - [JsonPropertyName("id")] - public string Id { get; set; } - - [JsonPropertyName("number")] - public decimal Number { get; set; } - - [JsonPropertyName("volume")] - public int? Volume { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("branchId")] - public string BranchId { get; set; } - - public string FullName { - get { - if (!Volume.HasValue) { - return Title; - } - - var shortName = $"Том {Volume}. Глава {(int)Number}"; - return string.IsNullOrWhiteSpace(Title) ? shortName : Title; - } - } -} - -public class RanobeOvhChapterContent { - [JsonPropertyName("type")] - public string Type { get; set; } - - [JsonPropertyName("text")] - public string Text { get; set; } - - [JsonPropertyName("marks")] - public RanobeOvhChapterMark[] Marks { get; set; } = []; - - [JsonPropertyName("content")] - public RanobeOvhChapterContent[] Content { get; set; } = []; - - [JsonPropertyName("attrs")] - public Dictionary Attrs { get; set; } = new(); -} - -public class RanobeOvhChapterMark { - [JsonPropertyName("type")] - public string Type { get; set; } -} diff --git a/Core/Types/RanobeOvh/RanobeOvhManga.cs b/Core/Types/RanobeOvh/RanobeOvhManga.cs deleted file mode 100644 index 69c07496..00000000 --- a/Core/Types/RanobeOvh/RanobeOvhManga.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeOvh; - -public class RanobeOvhManga { - [JsonPropertyName("name")] - public RanobeOvhMangaName Name { get; set; } - - [JsonPropertyName("poster")] - public string Poster { get; set; } - - [JsonPropertyName("description")] - public string Description { get; set; } -} \ No newline at end of file diff --git a/Core/Types/RanobeOvh/RanobeOvhMangaName.cs b/Core/Types/RanobeOvh/RanobeOvhMangaName.cs deleted file mode 100644 index b23a4f5e..00000000 --- a/Core/Types/RanobeOvh/RanobeOvhMangaName.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeOvh; - -public class RanobeOvhMangaName { - [JsonPropertyName("ru")] - public string Ru { get; set; } -} \ No newline at end of file diff --git a/Core/Types/RanobeOvh/RanobeOvhPage.cs b/Core/Types/RanobeOvh/RanobeOvhPage.cs deleted file mode 100644 index f743be1a..00000000 --- a/Core/Types/RanobeOvh/RanobeOvhPage.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeOvh; - -public class RanobeOvhPage { - [JsonPropertyName("id")] - public string Id { get; set; } - - [JsonPropertyName("image")] - public string Image { get; set; } -} \ No newline at end of file diff --git a/Core/Types/RanobeOvh/RanoveOvhTranslator.cs b/Core/Types/RanobeOvh/RanoveOvhTranslator.cs deleted file mode 100644 index 1fb9c9ac..00000000 --- a/Core/Types/RanobeOvh/RanoveOvhTranslator.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeOvh; - -public class RanoveOvhTranslator { - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("slug")] - public string Slug { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Ranobes/RanobesCookie.cs b/Core/Types/Ranobes/RanobesCookie.cs deleted file mode 100644 index 1749fa06..00000000 --- a/Core/Types/Ranobes/RanobesCookie.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Ranobes; - -public class RanobesCookie { - [JsonPropertyName("cookie")] - public string Cookie { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Renovels/RenovelsApiResponse.cs b/Core/Types/Renovels/RenovelsApiResponse.cs deleted file mode 100644 index bd9be845..00000000 --- a/Core/Types/Renovels/RenovelsApiResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Renovels; - -public class RenovelsApiResponse { - [JsonPropertyName("msg")] - public string Message { get; set; } - - [JsonPropertyName("content")] - public T Content { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Renovels/RenovelsAuthResponse.cs b/Core/Types/Renovels/RenovelsAuthResponse.cs deleted file mode 100644 index 6b9a31b5..00000000 --- a/Core/Types/Renovels/RenovelsAuthResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Renovels; - -public class RenovelsAuthResponse { - [JsonPropertyName("access_token")] - public string Token { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Renovels/RenovelsBranch.cs b/Core/Types/Renovels/RenovelsBranch.cs deleted file mode 100644 index b600b99b..00000000 --- a/Core/Types/Renovels/RenovelsBranch.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Renovels; - -public class RenovelsBranch { - [JsonPropertyName("id")] - public int Id { get; set; } - - [JsonPropertyName("publishers")] - public RenovelsPublisher[] Publishers { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Renovels/RenovelsChapter.cs b/Core/Types/Renovels/RenovelsChapter.cs deleted file mode 100644 index 00979bab..00000000 --- a/Core/Types/Renovels/RenovelsChapter.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -namespace Core.Types.Renovels; - -public class RenovelsChapter { - [JsonPropertyName("id")] - public int Id { get; set; } - - [JsonPropertyName("chapter")] - public string Chapter { get; set; } - - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("tome")] - public int Tome { get; set; } - - [JsonPropertyName("content")] - public string Content { get; set; } - - [JsonPropertyName("is_paid")] - public bool IsPaid { get; set; } - - [JsonPropertyName("is_bought")] - public bool? IsBought { get; set; } - - [JsonPropertyName("pages")] - public JsonArray Pages { get; set; } - - public string Title { - get { - var name = $"Том {Tome}. Глава {Chapter}"; - if (string.IsNullOrWhiteSpace(Name)) { - return name; - } - - return name + $" - {Name}"; - } - } -} \ No newline at end of file diff --git a/Core/Types/Renovels/RenovelsContent.cs b/Core/Types/Renovels/RenovelsContent.cs deleted file mode 100644 index acbf6a39..00000000 --- a/Core/Types/Renovels/RenovelsContent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Core.Types.Renovels; - -public class RenovelsContent { - [JsonPropertyName("cover")] - public Dictionary Cover { get; set; } - - [JsonPropertyName("main_name")] - public string MainName { get; set; } - - [JsonPropertyName("secondary_name")] - public string SecondaryName { get; set; } - - [JsonPropertyName("another_name")] - public string AnotherName { get; set; } - - [JsonPropertyName("description")] - public string Description { get; set; } - - [JsonPropertyName("branches")] - public RenovelsBranch[] Branches { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Renovels/RenovelsPage.cs b/Core/Types/Renovels/RenovelsPage.cs deleted file mode 100644 index a0271496..00000000 --- a/Core/Types/Renovels/RenovelsPage.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Renovels; - -public class RenovelsPage { - [JsonPropertyName("link")] - public string Link { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Renovels/RenovelsPublisher.cs b/Core/Types/Renovels/RenovelsPublisher.cs deleted file mode 100644 index 431930df..00000000 --- a/Core/Types/Renovels/RenovelsPublisher.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Renovels; - -public class RenovelsPublisher { - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("dir")] - public string Dir { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Renovels/RenovelsTocResponse.cs b/Core/Types/Renovels/RenovelsTocResponse.cs deleted file mode 100644 index ca3d56ed..00000000 --- a/Core/Types/Renovels/RenovelsTocResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Renovels; - -public class RenovelsTocResponse { - [JsonPropertyName("results")] - public RenovelsChapter[] Results { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Rulate/RulateAuthResponse.cs b/Core/Types/Rulate/RulateAuthResponse.cs deleted file mode 100644 index 69191ae6..00000000 --- a/Core/Types/Rulate/RulateAuthResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Rulate; - -public class RulateAuthResponse { - [JsonPropertyName("error")] - public string Error { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Rulate/RulateChapter.cs b/Core/Types/Rulate/RulateChapter.cs deleted file mode 100644 index ed4bc160..00000000 --- a/Core/Types/Rulate/RulateChapter.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Rulate; - -public class RulateChapter { - [JsonPropertyName("can_read")] - public bool CanRead { get; set; } - - [JsonPropertyName("content")] - public string Content { get; set; } -} \ No newline at end of file diff --git a/Core/Types/SocialLib/LibSocialToken.cs b/Core/Types/SocialLib/LibSocialToken.cs deleted file mode 100644 index 1b43c11f..00000000 --- a/Core/Types/SocialLib/LibSocialToken.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.SocialLib; - -public class LibSocialToken { - [JsonPropertyName("access_token")] - public string AccessToken { get; set; } -} \ No newline at end of file diff --git a/Core/Types/SocialLib/RanobeLibBookDetail.cs b/Core/Types/SocialLib/RanobeLibBookDetail.cs deleted file mode 100644 index c2ea44ef..00000000 --- a/Core/Types/SocialLib/RanobeLibBookDetail.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Core.Types.SocialLib; - -public class RanobeLibBookDetails { - [JsonPropertyName("data")] - public RLBDData Data { get; set; } -} - -public class RLBDData { - public RLBDData(int? id) { - Id = id; - } - - [JsonPropertyName("id")] - public int? Id { get; set; } - - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("rus_name")] - public string RusName { get; set; } - - [JsonPropertyName("slug_url")] - public string SlugUrl { get; set; } - - [JsonPropertyName("cover")] - public RLBDCover Cover { get; set; } - - [JsonPropertyName("authors")] - public List Authors { get; set; } - - [JsonPropertyName("summary")] - public string Summary { get; set; } -} - -public class RLBDCover { - [JsonPropertyName("default")] - public string Default { get; set; } -} - -public class RLBDAuthor { - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("slug_url")] - public string SlugUrl { get; set; } -} \ No newline at end of file diff --git a/Core/Types/SocialLib/SocialLibBookChapters.cs b/Core/Types/SocialLib/SocialLibBookChapters.cs deleted file mode 100644 index e91c2085..00000000 --- a/Core/Types/SocialLib/SocialLibBookChapters.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -namespace Core.Types.SocialLib; - -public class SocialLibBookChapters { - [JsonPropertyName("data")] - public List Chapters { get; set; } -} - -public class SocialLibBookChapterResponse { - [JsonPropertyName("data")] - public SocialLibBookChapter Data { get; set; } -} - -public class SocialLibChapterBranch { - [JsonPropertyName("branch_id")] - public long? BranchId { get; set; } -} - -public class SocialLibChapterAttachment { - [JsonPropertyName("url")] - public string Url { get; set; } - - [JsonPropertyName("name")] - public string Name { get; set; } -} - -public class SocialLibBookChapter { - [JsonPropertyName("volume")] - public string Volume { get; set; } - - [JsonPropertyName("number")] - public string Number { get; set; } - - [JsonPropertyName("branches")] - public List Branches { get; set; } - - [JsonPropertyName("attachments")] - public SocialLibChapterAttachment[] Attachments { get; set; } = []; - - [JsonPropertyName("pages")] - public SocialLibPage[] Pages { get; set; } = []; - - [JsonPropertyName("content")] - public JsonNode Content { get; set; } - - private string RawName { get; set; } - - [JsonPropertyName("name")] - public string Name { - get { - var name = $"Том {Volume} Глава {Number}"; - if (!string.IsNullOrWhiteSpace(RawName)) { - name += $" - {RawName}"; - } - - return name; - } - set { RawName = value; } - } -} - -public class SocialLibChapterContent { - [JsonPropertyName("type")] - public string Type { get; set; } - - [JsonPropertyName("text")] - public string Text { get; set; } - - [JsonPropertyName("marks")] - public SocialLibChapterMark[] Marks { get; set; } = []; - - [JsonPropertyName("content")] - public SocialLibChapterContent[] Content { get; set; } = []; - - [JsonPropertyName("attrs")] - public Dictionary Attrs { get; set; } = new(); -} - -public class SocialLibChapterMark { - [JsonPropertyName("type")] - public string Type { get; set; } -} - -public class SocialLibPage { - [JsonPropertyName("url")] - public string Url { get; set; } -} \ No newline at end of file diff --git a/Core/Types/SocialLib/SocialLibChapter.cs b/Core/Types/SocialLib/SocialLibChapter.cs deleted file mode 100644 index 2ec35780..00000000 --- a/Core/Types/SocialLib/SocialLibChapter.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.SocialLib; - -public class SocialLibChapter { - [JsonPropertyName("chapter_number")] - public string ChapterNumber { get; set; } - - [JsonPropertyName("chapter_volume")] - public int ChapterVolume { get; set; } - - [JsonPropertyName("chapter_name")] - public string ChapterName { get; set; } - - [JsonPropertyName("chapter_slug")] - public string ChapterSlug { get; set; } - - [JsonPropertyName("branch_id")] - public int? BranchId { get; set; } - - public string Name => $"Том {ChapterVolume} Глава {ChapterNumber} {ChapterName}".Trim(); -} \ No newline at end of file diff --git a/Core/Types/SocialLib/SocialLibChapterOld.cs b/Core/Types/SocialLib/SocialLibChapterOld.cs deleted file mode 100644 index b8b4ade2..00000000 --- a/Core/Types/SocialLib/SocialLibChapterOld.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.SocialLib; - -public class SocialLibChapterOld { - [JsonPropertyName("number")] - public string ChapterNumber { get; set; } - - [JsonPropertyName("volume")] - public string ChapterVolume { get; set; } - - [JsonPropertyName("name")] - public string ChapterName { get; set; } - - [JsonPropertyName("slug")] - public string ChapterSlug { get; set; } - - public string GetName() { - return $"Том {ChapterVolume} Глава {ChapterNumber} {ChapterName}".Trim(); - } -} \ No newline at end of file diff --git a/Core/Types/SocialLib/SocialLibChapters.cs b/Core/Types/SocialLib/SocialLibChapters.cs deleted file mode 100644 index afb78616..00000000 --- a/Core/Types/SocialLib/SocialLibChapters.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Core.Types.SocialLib; - -public class SocialLibChapters { - [JsonPropertyName("list")] - public List List { get; set; } -} \ No newline at end of file diff --git a/Core/Types/SocialLib/User.cs b/Core/Types/SocialLib/User.cs deleted file mode 100644 index eba2bd1d..00000000 --- a/Core/Types/SocialLib/User.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.SocialLib; - -public class User { - [JsonPropertyName("id")] - public long Id { get; set; } -} \ No newline at end of file diff --git a/Core/Types/SocialLib/WindowData.cs b/Core/Types/SocialLib/WindowData.cs deleted file mode 100644 index f4fb0ce1..00000000 --- a/Core/Types/SocialLib/WindowData.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.SocialLib; - -public class WindowData { - [JsonPropertyName("chapters")] - public SocialLibChapters Chapters { get; set; } - - [JsonPropertyName("user")] - public User User { get; set; } -} \ No newline at end of file diff --git a/Core/Types/StrokiMts/StrokiMtsApiMultiResponse.cs b/Core/Types/StrokiMts/StrokiMtsApiMultiResponse.cs deleted file mode 100644 index ab52c4fe..00000000 --- a/Core/Types/StrokiMts/StrokiMtsApiMultiResponse.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsApiMultiResponse { - [JsonPropertyName("items")] - public List Items { get; set; } -} \ No newline at end of file diff --git a/Core/Types/StrokiMts/StrokiMtsApiResponse.cs b/Core/Types/StrokiMts/StrokiMtsApiResponse.cs deleted file mode 100644 index ed72493b..00000000 --- a/Core/Types/StrokiMts/StrokiMtsApiResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsApiResponse { - [JsonPropertyName("data")] - public T Data { get; set; } -} \ No newline at end of file diff --git a/Core/Types/StrokiMts/StrokiMtsAuthor.cs b/Core/Types/StrokiMts/StrokiMtsAuthor.cs deleted file mode 100644 index 6ed3d3b6..00000000 --- a/Core/Types/StrokiMts/StrokiMtsAuthor.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsAuthor { - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("friendlyUrl")] - public string FriendlyUrl { get; set; } -} \ No newline at end of file diff --git a/Core/Types/StrokiMts/StrokiMtsBookItem.cs b/Core/Types/StrokiMts/StrokiMtsBookItem.cs deleted file mode 100644 index f3b5a107..00000000 --- a/Core/Types/StrokiMts/StrokiMtsBookItem.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsBookItem { - [JsonPropertyName("id")] - public long Id { get; set; } - - [JsonPropertyName("annotation")] - public string Annotation { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("imageUrl")] - public Dictionary ImageUrl { get; set; } - - [JsonPropertyName("authors")] - public List Authors { get; set; } - - [JsonPropertyName("contentInfo")] - public StrokiMtsContentInfo ContentInfo { get; set; } -} \ No newline at end of file diff --git a/Core/Types/StrokiMts/StrokiMtsContentInfo.cs b/Core/Types/StrokiMts/StrokiMtsContentInfo.cs deleted file mode 100644 index a8995103..00000000 --- a/Core/Types/StrokiMts/StrokiMtsContentInfo.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsContentInfo { - [JsonPropertyName("fileType")] - public string FileType { get; set; } -} \ No newline at end of file diff --git a/Core/Types/StrokiMts/StrokiMtsFile.cs b/Core/Types/StrokiMts/StrokiMtsFile.cs deleted file mode 100644 index 717b912f..00000000 --- a/Core/Types/StrokiMts/StrokiMtsFile.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsFile { - [JsonPropertyName("fileId")] - public long FileId { get; set; } -} \ No newline at end of file diff --git a/Core/Types/StrokiMts/StrokiMtsFileUrl.cs b/Core/Types/StrokiMts/StrokiMtsFileUrl.cs deleted file mode 100644 index 26956eda..00000000 --- a/Core/Types/StrokiMts/StrokiMtsFileUrl.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsFileUrl { - [JsonPropertyName("url")] - public string Url { get; set; } -} \ No newline at end of file diff --git a/Core/Types/StrokiMts/StrokiMtsFiles.cs b/Core/Types/StrokiMts/StrokiMtsFiles.cs deleted file mode 100644 index 639a42c2..00000000 --- a/Core/Types/StrokiMts/StrokiMtsFiles.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsFiles { - [JsonPropertyName("preview")] - public StrokiMtsFile Preview { get; set; } - - [JsonPropertyName("full")] - public StrokiMtsFile[] Full { get; set; } -} \ No newline at end of file diff --git a/Core/Types/StrokiMts/StrokiMtsMultiItem.cs b/Core/Types/StrokiMts/StrokiMtsMultiItem.cs deleted file mode 100644 index 43822a27..00000000 --- a/Core/Types/StrokiMts/StrokiMtsMultiItem.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsMultiItem { - [JsonPropertyName("textBook")] - public StrokiMtsBookItem TextBook { get; set; } - - [JsonPropertyName("audioBook")] - public StrokiMtsBookItem AudioBook { get; set; } - - [JsonPropertyName("comics")] - public StrokiMtsBookItem ComicBook { get; set; } - - [JsonPropertyName("pressa")] - public StrokiMtsBookItem PressBook { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Wattpad/WattpadInfo.cs b/Core/Types/Wattpad/WattpadInfo.cs deleted file mode 100644 index 328fe19f..00000000 --- a/Core/Types/Wattpad/WattpadInfo.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Wattpad; - -public class WattpadInfo { - [JsonPropertyName("user")] - public WattpadUser User { get; set; } - - [JsonPropertyName("cover")] - public string Cover { get; set; } - - [JsonPropertyName("parts")] - public WattpadPart[] Parts { get; set; } - - [JsonPropertyName("description")] - public string Description { get; set; } - - [JsonPropertyName("title")] - public string Title { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Wattpad/WattpadMeta.cs b/Core/Types/Wattpad/WattpadMeta.cs deleted file mode 100644 index f948dc7d..00000000 --- a/Core/Types/Wattpad/WattpadMeta.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Core.Types.Wattpad; - -public class WattpadMeta { - public string StoryId { get; set; } -} \ No newline at end of file diff --git a/Core/Types/Wattpad/WattpadUser.cs b/Core/Types/Wattpad/WattpadUser.cs deleted file mode 100644 index 5743a991..00000000 --- a/Core/Types/Wattpad/WattpadUser.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Wattpad; - -public class WattpadUser { - [JsonPropertyName("name")] - public string Name { get; set; } -} \ No newline at end of file diff --git a/Core/Types/WuxiaWorld/WuxiaWorldToc.cs b/Core/Types/WuxiaWorld/WuxiaWorldToc.cs deleted file mode 100644 index a8e7f56a..00000000 --- a/Core/Types/WuxiaWorld/WuxiaWorldToc.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.WuxiaWorld; - -public class WuxiaWorldToc { - [JsonPropertyName("post_name")] - public string PostName { get; set; } -} \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 00000000..297468eb --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,9 @@ + + + net9.0 + disable + enable + latestmajor + 2.8.2 + + \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 00000000..2d326696 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,24 @@ + + + true + false + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Elib2Ebook.sln b/Elib2Ebook.sln index 134c4c5b..0d2a4ac9 100644 --- a/Elib2Ebook.sln +++ b/Elib2Ebook.sln @@ -1,28 +1,89 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{3576A033-7429-4DE3-BBE3-CD703A4564ED}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2EbookWeb", "Elib2EbookWeb\Elib2EbookWeb.csproj", "{C3CCD71A-37D0-4665-A628-AC6185313A4C}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "common", "common", "{D12CC161-970E-46FB-8C3E-DE2A78304283}" + ProjectSection(SolutionItems) = preProject + Directory.Build.props = Directory.Build.props + Directory.Packages.props = Directory.Packages.props + EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2EbookCli", "Elib2EbookCli\Elib2EbookCli.csproj", "{04A5E9A6-9EA9-4771-9766-1241F8341A9B}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "src\Core\Core.csproj", "{3576A033-7429-4DE3-BBE3-CD703A4564ED}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2EbookWeb", "src\Elib2EbookWeb\Elib2EbookWeb.csproj", "{C3CCD71A-37D0-4665-A628-AC6185313A4C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2EbookCli", "src\Elib2EbookCli\Elib2EbookCli.csproj", "{04A5E9A6-9EA9-4771-9766-1241F8341A9B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0C88DD14-F956-CE84-757C-A364CCF449FC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Tests", "tests\Core.Tests\Core.Tests.csproj", "{97B6CA29-9704-4A44-8879-E7015403E743}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Debug|x64.ActiveCfg = Debug|Any CPU + {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Debug|x64.Build.0 = Debug|Any CPU + {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Debug|x86.ActiveCfg = Debug|Any CPU + {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Debug|x86.Build.0 = Debug|Any CPU {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Release|Any CPU.ActiveCfg = Release|Any CPU {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Release|Any CPU.Build.0 = Release|Any CPU + {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Release|x64.ActiveCfg = Release|Any CPU + {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Release|x64.Build.0 = Release|Any CPU + {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Release|x86.ActiveCfg = Release|Any CPU + {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Release|x86.Build.0 = Release|Any CPU {C3CCD71A-37D0-4665-A628-AC6185313A4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C3CCD71A-37D0-4665-A628-AC6185313A4C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3CCD71A-37D0-4665-A628-AC6185313A4C}.Debug|x64.ActiveCfg = Debug|Any CPU + {C3CCD71A-37D0-4665-A628-AC6185313A4C}.Debug|x64.Build.0 = Debug|Any CPU + {C3CCD71A-37D0-4665-A628-AC6185313A4C}.Debug|x86.ActiveCfg = Debug|Any CPU + {C3CCD71A-37D0-4665-A628-AC6185313A4C}.Debug|x86.Build.0 = Debug|Any CPU {C3CCD71A-37D0-4665-A628-AC6185313A4C}.Release|Any CPU.ActiveCfg = Release|Any CPU {C3CCD71A-37D0-4665-A628-AC6185313A4C}.Release|Any CPU.Build.0 = Release|Any CPU + {C3CCD71A-37D0-4665-A628-AC6185313A4C}.Release|x64.ActiveCfg = Release|Any CPU + {C3CCD71A-37D0-4665-A628-AC6185313A4C}.Release|x64.Build.0 = Release|Any CPU + {C3CCD71A-37D0-4665-A628-AC6185313A4C}.Release|x86.ActiveCfg = Release|Any CPU + {C3CCD71A-37D0-4665-A628-AC6185313A4C}.Release|x86.Build.0 = Release|Any CPU {04A5E9A6-9EA9-4771-9766-1241F8341A9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {04A5E9A6-9EA9-4771-9766-1241F8341A9B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04A5E9A6-9EA9-4771-9766-1241F8341A9B}.Debug|x64.ActiveCfg = Debug|Any CPU + {04A5E9A6-9EA9-4771-9766-1241F8341A9B}.Debug|x64.Build.0 = Debug|Any CPU + {04A5E9A6-9EA9-4771-9766-1241F8341A9B}.Debug|x86.ActiveCfg = Debug|Any CPU + {04A5E9A6-9EA9-4771-9766-1241F8341A9B}.Debug|x86.Build.0 = Debug|Any CPU {04A5E9A6-9EA9-4771-9766-1241F8341A9B}.Release|Any CPU.ActiveCfg = Release|Any CPU {04A5E9A6-9EA9-4771-9766-1241F8341A9B}.Release|Any CPU.Build.0 = Release|Any CPU + {04A5E9A6-9EA9-4771-9766-1241F8341A9B}.Release|x64.ActiveCfg = Release|Any CPU + {04A5E9A6-9EA9-4771-9766-1241F8341A9B}.Release|x64.Build.0 = Release|Any CPU + {04A5E9A6-9EA9-4771-9766-1241F8341A9B}.Release|x86.ActiveCfg = Release|Any CPU + {04A5E9A6-9EA9-4771-9766-1241F8341A9B}.Release|x86.Build.0 = Release|Any CPU + {97B6CA29-9704-4A44-8879-E7015403E743}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {97B6CA29-9704-4A44-8879-E7015403E743}.Debug|Any CPU.Build.0 = Debug|Any CPU + {97B6CA29-9704-4A44-8879-E7015403E743}.Debug|x64.ActiveCfg = Debug|Any CPU + {97B6CA29-9704-4A44-8879-E7015403E743}.Debug|x64.Build.0 = Debug|Any CPU + {97B6CA29-9704-4A44-8879-E7015403E743}.Debug|x86.ActiveCfg = Debug|Any CPU + {97B6CA29-9704-4A44-8879-E7015403E743}.Debug|x86.Build.0 = Debug|Any CPU + {97B6CA29-9704-4A44-8879-E7015403E743}.Release|Any CPU.ActiveCfg = Release|Any CPU + {97B6CA29-9704-4A44-8879-E7015403E743}.Release|Any CPU.Build.0 = Release|Any CPU + {97B6CA29-9704-4A44-8879-E7015403E743}.Release|x64.ActiveCfg = Release|Any CPU + {97B6CA29-9704-4A44-8879-E7015403E743}.Release|x64.Build.0 = Release|Any CPU + {97B6CA29-9704-4A44-8879-E7015403E743}.Release|x86.ActiveCfg = Release|Any CPU + {97B6CA29-9704-4A44-8879-E7015403E743}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3576A033-7429-4DE3-BBE3-CD703A4564ED} = {A1B2C3D4-E5F6-7890-ABCD-EF1234567890} + {C3CCD71A-37D0-4665-A628-AC6185313A4C} = {A1B2C3D4-E5F6-7890-ABCD-EF1234567890} + {04A5E9A6-9EA9-4771-9766-1241F8341A9B} = {A1B2C3D4-E5F6-7890-ABCD-EF1234567890} + {97B6CA29-9704-4A44-8879-E7015403E743} = {0C88DD14-F956-CE84-757C-A364CCF449FC} EndGlobalSection EndGlobal diff --git a/Elib2EbookCli/ConsoleLogger.cs b/Elib2EbookCli/ConsoleLogger.cs deleted file mode 100644 index 40511993..00000000 --- a/Elib2EbookCli/ConsoleLogger.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Microsoft.Extensions.Logging; - -namespace Elib2EbookCli; - -public class ConsoleLogger : ILogger { - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { - Console.WriteLine($"{formatter(state, exception)}"); - } - - bool ILogger.IsEnabled(LogLevel logLevel) { - return true; - } - - public IDisposable? BeginScope(TState state) where TState : notnull => default!; -} \ No newline at end of file diff --git a/Elib2EbookCli/Elib2EbookCli.csproj b/Elib2EbookCli/Elib2EbookCli.csproj deleted file mode 100644 index c1d7ae4a..00000000 --- a/Elib2EbookCli/Elib2EbookCli.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - Exe - net9.0 - enable - enable - - - - - - - - - - - - - .dockerignore - - - - diff --git a/Elib2EbookWeb/Components/App.razor b/Elib2EbookWeb/Components/App.razor deleted file mode 100644 index 6bd64bf6..00000000 --- a/Elib2EbookWeb/Components/App.razor +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Elib2EbookWeb/Components/PWA.razor b/Elib2EbookWeb/Components/PWA.razor deleted file mode 100644 index 9ee3d191..00000000 --- a/Elib2EbookWeb/Components/PWA.razor +++ /dev/null @@ -1,22 +0,0 @@ -Установить приложение - - \ No newline at end of file diff --git a/Elib2EbookWeb/Components/ShortBook.razor b/Elib2EbookWeb/Components/ShortBook.razor deleted file mode 100644 index d0362ba3..00000000 --- a/Elib2EbookWeb/Components/ShortBook.razor +++ /dev/null @@ -1,63 +0,0 @@ -@using Core.Types.Book - - - - - - - - - @Book.Title - - @if (Book.Author != default) { - @if (Book.Author.Url == default) { - Автор: @Book.Author.Name - } else { - Автор: @Book.Author.Name - } - } - - @if (Book.Seria != default) { - @if (Book.Seria?.Url == default) { - Серия: @Seria - } else { - Серия: @Seria - } - } - - @if (!string.IsNullOrWhiteSpace(Book.Annotation)) { - @((MarkupString)Book.Annotation) - } - - - - - -@code { - [Parameter] - public required Book Book { get; set; } - - private string? _cover; - - private string? _coverPath; - - private string Seria => Book.Seria?.Name + (string.IsNullOrWhiteSpace(Book.Seria?.Number) ? string.Empty : $" #{Book.Seria.Number}"); - - protected override Task OnInitializedAsync() { - InitCover(); - return base.OnInitializedAsync(); - } - - protected override Task OnParametersSetAsync() { - if (Book.Cover?.FilePath != _coverPath) { - InitCover(); - } - - return base.OnParametersSetAsync(); - } - - private void InitCover() { - _cover = Book.Cover != default && Book.Cover.Content.Length > 0 ? $"data:image/png;base64, {Convert.ToBase64String(Book.Cover.Content)}" : _cover; - _coverPath = Book.Cover?.FilePath; - } -} \ No newline at end of file diff --git a/Elib2EbookWeb/Elib2EbookWeb.csproj b/Elib2EbookWeb/Elib2EbookWeb.csproj deleted file mode 100644 index 11ea8951..00000000 --- a/Elib2EbookWeb/Elib2EbookWeb.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - net9.0 - enable - enable - Linux - - - - - - - - - - - - - .dockerignore - - - PreserveNewest - - - - diff --git a/Elib2EbookWeb/Misc/SbWriter.cs b/Elib2EbookWeb/Misc/SbWriter.cs deleted file mode 100644 index ad8eb55a..00000000 --- a/Elib2EbookWeb/Misc/SbWriter.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Elib2EbookWeb.Misc; - -public class ActionLogger : ILogger { - private readonly Action _action; - - public ActionLogger(Action action) { - _action = action; - } - - public IDisposable? BeginScope(TState state) where TState : notnull => default!; - - public bool IsEnabled(LogLevel logLevel) => true; - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { - _action($"{formatter(state, exception)}"); - } -} \ No newline at end of file diff --git a/Elib2EbookWeb/Properties/launchSettings.json b/Elib2EbookWeb/Properties/launchSettings.json deleted file mode 100644 index 364efbdc..00000000 --- a/Elib2EbookWeb/Properties/launchSettings.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/launchsettings.json", - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:41898", - "sslPort": 44383 - } - }, - "profiles": { - "http": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "applicationUrl": "http://localhost:5271", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "applicationUrl": "https://localhost:7188;http://localhost:5271", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } - } diff --git a/README.md b/README.md index 49938765..1ae3b1f3 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,13 @@ # 📚 Elib2Ebook [![License](https://img.shields.io/github/license/OnlyFart/Elib2Ebook.svg?style=flat-square)](LICENSE) -[![Forks](https://img.shields.io/github/forks/OnlyFart/Elib2Ebook.svg?style=flat-square)](https://github.com/OnlyFart/Elib2Ebook/network) -[![Latest Tag](https://img.shields.io/github/v/tag/OnlyFart/Elib2Ebook.svg?style=flat-square)](https://github.com/OnlyFart/Elib2Ebook/releases/latest) +[![.NET Build, Test & Publish](https://github.com/OnlyFart/Elib2Ebook/actions/workflows/dotnet.yml/badge.svg?style=flat-square)](https://github.com/OnlyFart/Elib2Ebook/actions/workflows/dotnet.yml) +[![GitHub Release](https://img.shields.io/github/v/release/OnlyFart/Elib2Ebook?style=flat-square)](https://github.com/OnlyFart/Elib2Ebook/releases/latest) ![Docker Pulls (CLI)](https://img.shields.io/docker/pulls/onlyfart/elib2ebookcli?style=flat-square&label=docker%20pulls%20(cli)) ![Docker Pulls (Web)](https://img.shields.io/docker/pulls/onlyfart/elib2ebookweb?style=flat-square&label=docker%20pulls%20(web)) ![GitHub Downloads (All)](https://img.shields.io/github/downloads/onlyfart/elib2ebook/total?style=flat-square) **Elib2Ebook** — консольная утилита и веб-приложение для скачивания книг с популярных онлайн-библиотек и литературных сайтов. Поддерживает сохранение в форматах EPUB, FB2, CBZ, JSON, TXT. - --- ## Оглавление diff --git a/Core/Configs/BookGetterConfig.cs b/src/Core/Configs/BookGetterConfig.cs similarity index 73% rename from Core/Configs/BookGetterConfig.cs rename to src/Core/Configs/BookGetterConfig.cs index 5f24d16d..3e221e66 100644 --- a/Core/Configs/BookGetterConfig.cs +++ b/src/Core/Configs/BookGetterConfig.cs @@ -1,71 +1,85 @@ -using System; -using System.Net; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; +using System.Net; using Core.Extensions; using Core.Misc.TempFolder; using FlareSolverrSharp; using Microsoft.Extensions.Logging; -namespace Core.Configs; +namespace Core.Configs; -public class BookGetterConfig : IDisposable { +public class BookGetterConfig : IDisposable +{ public readonly ILogger Logger; - private class RedirectHandler : HttpClientHandler { + private class RedirectHandler : HttpClientHandler + { private readonly ILogger _logger; public int Delay { get; set; } - public RedirectHandler(ILogger logger) { + public RedirectHandler(ILogger logger) + { _logger = logger; } - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - try { + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + try + { var responseMessage = await base.SendAsync(request, cancellationToken); - if (responseMessage is { + if (responseMessage is + { StatusCode: HttpStatusCode.Redirect or HttpStatusCode.PermanentRedirect or HttpStatusCode.MovedPermanently or HttpStatusCode.Moved, Headers.Location: not null - }) { + }) + { request = new HttpRequestMessage(request.Method, responseMessage.Headers.Location); responseMessage = await base.SendAsync(request, cancellationToken); } - if (responseMessage.StatusCode == HttpStatusCode.TooManyRequests) { + if (responseMessage.StatusCode == HttpStatusCode.TooManyRequests) + { responseMessage.StatusCode = HttpStatusCode.Forbidden; } return responseMessage; - } catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException) { + } + catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException) + { _logger.LogInformation("Сервер не успевает ответить. Попробуйте увеличить Timeout с помощью параметра -t"); throw; - } catch (TaskCanceledException ex) { + } + catch (TaskCanceledException ex) + { _logger.LogInformation(ex, "Сервер не успевает ответить. Попробуйте увеличить Timeout с помощью параметра -t"); throw; - } catch (Exception ex) { + } + catch (Exception ex) + { _logger.LogInformation(ex.Message); throw; - } finally { - if (Delay > 0) { + } + finally + { + if (Delay > 0) + { _logger.LogInformation($"Жду {Delay} секунд(-ы)"); await Task.Delay(TimeSpan.FromSeconds(Delay), cancellationToken); } } } } - + public HttpClient Client { get; } public Options Options { get; } - + public CookieContainer CookieContainer { get; } - + public bool HasCredentials => !string.IsNullOrWhiteSpace(Options.Login) && !string.IsNullOrWhiteSpace(Options.Password); public TempFolder TempFolder { get; } - public BookGetterConfig(Options options, HttpClient client, CookieContainer cookieContainer, TempFolder tempFolder, ILogger logger) { + public BookGetterConfig(Options options, HttpClient client, CookieContainer cookieContainer, TempFolder tempFolder, ILogger logger) + { Logger = logger; Client = client; CookieContainer = cookieContainer; @@ -73,21 +87,25 @@ public BookGetterConfig(Options options, HttpClient client, CookieContainer cook TempFolder = tempFolder; } - public void Dispose() { + public void Dispose() + { Client?.Dispose(); TempFolder?.Dispose(); } - - public static BookGetterConfig GetDefault(Options options, ILogger logger) { + + public static BookGetterConfig GetDefault(Options options, ILogger logger) + { var cookieContainer = new CookieContainer(); var client = GetClient(options, cookieContainer, logger); - return new BookGetterConfig(options, client, cookieContainer, TempFolderFactory.Create(options.TempPath), logger); + return new BookGetterConfig(options, client, cookieContainer, TempFolderFactory.Create(options.TempPath), logger); } - - private static HttpClient GetClient(Options options, CookieContainer container, ILogger logger) { - var handler = new RedirectHandler(logger) { - AutomaticDecompression = DecompressionMethods.GZip | + + private static HttpClient GetClient(Options options, CookieContainer container, ILogger logger) + { + var handler = new RedirectHandler(logger) + { + AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate | DecompressionMethods.Brotli, ServerCertificateCustomValidationCallback = (_, _, _, _) => true, @@ -97,31 +115,34 @@ private static HttpClient GetClient(Options options, CookieContainer container, Delay = options.Delay }; - if (!string.IsNullOrEmpty(options.Proxy)) { + if (!string.IsNullOrEmpty(options.Proxy)) + { handler.Proxy = new WebProxy(options.Proxy.AsUri()); handler.UseProxy = true; } - if (!string.IsNullOrWhiteSpace(options.Flare)) { - var chandler = new ClearanceHandler(options.Flare) { - MaxTimeout = options.Timeout * 1000, - InnerHandler = handler + if (!string.IsNullOrWhiteSpace(options.Flare)) + { + var chandler = new ClearanceHandler(options.Flare) + { + MaxTimeout = options.Timeout * 1000, InnerHandler = handler }; - - if (!string.IsNullOrEmpty(options.Proxy)) { + + if (!string.IsNullOrEmpty(options.Proxy)) + { chandler.ProxyUrl = options.Proxy; } - + var cclient = new HttpClient(chandler); cclient.Timeout = TimeSpan.FromSeconds(options.Timeout); - + return cclient; } var client = new HttpClient(handler); - + client.Timeout = TimeSpan.FromSeconds(options.Timeout); - + return client; } -} \ No newline at end of file +} diff --git a/Core/Configs/Options.cs b/src/Core/Configs/Options.cs similarity index 74% rename from Core/Configs/Options.cs rename to src/Core/Configs/Options.cs index 6b7d8989..ab66c64a 100644 --- a/Core/Configs/Options.cs +++ b/src/Core/Configs/Options.cs @@ -1,71 +1,62 @@ -using System.Collections.Generic; -using System.Linq; using CommandLine; using Core.Misc; -namespace Core.Configs; +namespace Core.Configs; -public class Options { +public class Options +{ public Options() { } - public Options(IEnumerable url) { + public Options(IEnumerable url) + { Url = url; } [Option('u', "url", Required = true, HelpText = "Ссылка на книгу", Separator = ',')] public IEnumerable Url { get; set; } - + [Option('f', "format", Required = true, HelpText = "Формат для сохранения книги. Допустимые значения: epub, fb2, cbz, json", Separator = ',')] public IEnumerable Format { get; set; } - - [Option('l', "login", Required = false, HelpText = "Логин от системы")] - public string Login { get; set; } - - [Option('p', "password", Required = false, HelpText = "Пароль от системы")] - public string Password { get; set; } - - [Option("proxy", Required = false, HelpText = "Прокси в формате :")] - public string Proxy { get; set; } - - [Option("flare", Required = false, HelpText = "Адрес flaresolverr")] - public string Flare { get; set; } - + + [Option('l', "login", Required = false, HelpText = "Логин от системы")] public string Login { get; set; } + + [Option('p', "password", Required = false, HelpText = "Пароль от системы")] public string Password { get; set; } + + [Option("proxy", Required = false, HelpText = "Прокси в формате :")] public string Proxy { get; set; } + + [Option("flare", Required = false, HelpText = "Адрес flaresolverr")] public string Flare { get; set; } + [Option('s', "save", Required = false, HelpText = "Директория для сохранения книги")] public string SavePath { get; set; } [Option('c', "cover", Required = false, HelpText = "Сохранить обложку книги в отдельный файл")] public bool Cover { get; set; } - + [Option('t', "timeout", Required = false, HelpText = "Timeout для запросов в секундах", Default = 120)] public int Timeout { get; set; } - + [Option('d', "delay", Required = false, HelpText = "Задержка между запросами в секундах", Default = 0)] public int Delay { get; set; } - - [Option("no-image", Required = false, HelpText = "Не загружать картинки")] - public bool NoImage { get; set; } - - [Option("start", Required = false, HelpText = "Стартовый номер главы")] - public int? Start { get; set; } - - [Option("end", Required = false, HelpText = "Конечный номер главы")] - public int? End { get; set; } - - [Option("start-name", Required = false, HelpText = "Стартовое название главы")] - public string StartName { get; set; } - - [Option("end-name", Required = false, HelpText = "Конечное название главы")] - public string EndName { get; set; } + + [Option("no-image", Required = false, HelpText = "Не загружать картинки")] public bool NoImage { get; set; } + + [Option("start", Required = false, HelpText = "Стартовый номер главы")] public int? Start { get; set; } + + [Option("end", Required = false, HelpText = "Конечный номер главы")] public int? End { get; set; } + + [Option("start-name", Required = false, HelpText = "Стартовое название главы")] public string StartName { get; set; } + + [Option("end-name", Required = false, HelpText = "Конечное название главы")] public string EndName { get; set; } [Option("temp", Required = false, HelpText = "Папка для временного хранения картинок")] public string TempPath { get; set; } - + [Option("save-temp", Required = false, HelpText = "Сохранять временные файлы", Default = false)] public bool SaveTemp { get; set; } - + [Option("no-chapters", Required = false, HelpText = "Не загружать главы", Default = false)] public bool NoChapters { get; set; } - + [Option("additional", Required = false, HelpText = "Сохранить дополнительные файлы", Default = false)] public bool Additional { get; set; } @@ -74,8 +65,9 @@ public Options(IEnumerable url) { [Option("book-name-pattern", Required = false, HelpText = "Шаблон имени файла.", Default = "{Author.Name} - {Book.Title}")] public string BookNamePattern { get; set; } = "{Author.Name} - {Book.Title}"; - - public bool HasAdditionalType(AdditionalTypeEnum type) => Additional && (AdditionalTypes == default || !AdditionalTypes.Any() || AdditionalTypes.Contains(type)); + + public bool HasAdditionalType(AdditionalTypeEnum type) + => Additional && (AdditionalTypes == null || !AdditionalTypes.Any() || AdditionalTypes.Contains(type)); public string ResourcesPath => "Patterns"; -} \ No newline at end of file +} diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj new file mode 100644 index 00000000..39786beb --- /dev/null +++ b/src/Core/Core.csproj @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + Always + + + + + + External\EpubSharp.dll + + + + diff --git a/Core/Extensions/FileProviderExtensions.cs b/src/Core/Extensions/FileProviderExtensions.cs similarity index 70% rename from Core/Extensions/FileProviderExtensions.cs rename to src/Core/Extensions/FileProviderExtensions.cs index 3df7a5ec..6d410805 100644 --- a/Core/Extensions/FileProviderExtensions.cs +++ b/src/Core/Extensions/FileProviderExtensions.cs @@ -1,32 +1,34 @@ -using System.Collections.Generic; -using System.IO; using System.IO.Enumeration; -using System.Linq; using Microsoft.Extensions.FileProviders; namespace Core.Extensions; -public static class FileProviderExtensions { - public static byte[] ReadAllBytes(this IFileInfo fileInfo) { +public static class FileProviderExtensions +{ + public static byte[] ReadAllBytes(this IFileInfo fileInfo) + { using var ms = new MemoryStream(); using var source = fileInfo.CreateReadStream(); source.CopyTo(ms); return ms.ToArray(); } - public static string ReadAllText(this IFileInfo fileInfo) { + public static string ReadAllText(this IFileInfo fileInfo) + { using var source = fileInfo.CreateReadStream(); using var sr = new StreamReader(source); return sr.ReadToEnd(); } - public static string ReadAllText(this IFileProvider fileProvider, string path) { + public static string ReadAllText(this IFileProvider fileProvider, string path) + { var file = fileProvider.GetFileInfo(path); return file.ReadAllText(); } - public static IEnumerable GetFiles(this IFileProvider fileProvider, string directory, string searchPattern) { + public static IEnumerable GetFiles(this IFileProvider fileProvider, string directory, string searchPattern) + { return fileProvider.GetDirectoryContents(directory) .Where(file => FileSystemName.MatchesSimpleExpression(searchPattern, file.Name)); } -} \ No newline at end of file +} diff --git a/Core/Extensions/HtmlDocumentExtensions.cs b/src/Core/Extensions/HtmlDocumentExtensions.cs similarity index 70% rename from Core/Extensions/HtmlDocumentExtensions.cs rename to src/Core/Extensions/HtmlDocumentExtensions.cs index f8376966..33f3c818 100644 --- a/Core/Extensions/HtmlDocumentExtensions.cs +++ b/src/Core/Extensions/HtmlDocumentExtensions.cs @@ -1,14 +1,13 @@ -using System; -using System.IO; -using System.Linq; using System.Xml; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; -namespace Core.Extensions; +namespace Core.Extensions; -public static class HtmlDocumentExtensions { - public static string AsString(this HtmlDocument self) { +public static class HtmlDocumentExtensions +{ + public static string AsString(this HtmlDocument self) + { using var sw = new StringWriter(); using var xw = new XmlTextWriter(sw); self.Save(xw); @@ -16,43 +15,52 @@ public static string AsString(this HtmlDocument self) { return sw.ToString(); } - public static string GetTextBySelector(this HtmlDocument doc, string selector) { + public static string GetTextBySelector(this HtmlDocument doc, string selector) + { return doc.DocumentNode.GetTextBySelector(selector); } - - public static string GetTextBySelector(this HtmlNode node, string selector) { + + public static string GetTextBySelector(this HtmlNode node, string selector) + { return node.QuerySelector(selector).GetText(); } - - public static string GetText(this HtmlNode node) { + + public static string GetText(this HtmlNode node) + { return node?.InnerText?.HtmlDecode(); } - public static HtmlDocument RemoveNodes(this HtmlDocument doc, Func predicate) { + public static HtmlDocument RemoveNodes(this HtmlDocument doc, Func predicate) + { doc.DocumentNode.RemoveNodes(predicate); return doc; } - - public static HtmlNode RemoveNodes(this HtmlNode self, Func predicate) { + + public static HtmlNode RemoveNodes(this HtmlNode self, Func predicate) + { var toRemove = self.ChildNodes.Where(predicate).ToList(); - foreach (var node in toRemove) { + foreach (var node in toRemove) + { node.Remove(); } return self; } - - public static HtmlNode RemoveNodes(this HtmlNode self, string selector) { + + public static HtmlNode RemoveNodes(this HtmlNode self, string selector) + { var toRemove = self.QuerySelectorAll(selector); - foreach (var node in toRemove) { + foreach (var node in toRemove) + { node.Remove(); } return self; } - - public static HtmlDocument RemoveNodes(this HtmlDocument self, string selector) { + + public static HtmlDocument RemoveNodes(this HtmlDocument self, string selector) + { self.DocumentNode.RemoveNodes(selector); return self; } -} \ No newline at end of file +} diff --git a/Core/Extensions/HttpClientExtensions.cs b/src/Core/Extensions/HttpClientExtensions.cs similarity index 64% rename from Core/Extensions/HttpClientExtensions.cs rename to src/Core/Extensions/HttpClientExtensions.cs index d4daf214..9b5bb159 100644 --- a/Core/Extensions/HttpClientExtensions.cs +++ b/src/Core/Extensions/HttpClientExtensions.cs @@ -1,31 +1,34 @@ -using System; -using System.IO; using System.Net; -using System.Net.Http; using System.Text; using System.Text.Json; -using System.Threading.Tasks; using HtmlAgilityPack; -namespace Core.Extensions; +namespace Core.Extensions; -public static class HttpClientExtensions { +public static class HttpClientExtensions +{ private const int MAX_TRY_COUNT = 5; private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(1); - private static TimeSpan GetTimeout(TimeSpan errorTimeout) { - return errorTimeout == default ? DefaultTimeout : errorTimeout; + private static TimeSpan GetTimeout(TimeSpan errorTimeout) + { + return errorTimeout == TimeSpan.Zero ? DefaultTimeout : errorTimeout; } - public static async Task GetWithTriesAsync(this HttpClient client, Uri url, TimeSpan errorTimeout = default) { + public static async Task GetWithTriesAsync(this HttpClient client, Uri url, TimeSpan errorTimeout = default) + { Exception lastEx = null; - - for (var i = 0; i < MAX_TRY_COUNT; i++) { - try { + + for (var i = 0; i < MAX_TRY_COUNT; i++) + { + try + { var response = await client.GetAsync(url); - if (response.StatusCode != HttpStatusCode.OK) { - if (i == MAX_TRY_COUNT - 1) { + if (response.StatusCode != HttpStatusCode.OK) + { + if (i == MAX_TRY_COUNT - 1) + { return response; } @@ -35,28 +38,36 @@ public static async Task GetWithTriesAsync(this HttpClient } return response; - } catch (Exception ex) { + } + catch (Exception ex) + { lastEx = ex; await Task.Delay(GetTimeout(errorTimeout)); } } - if (lastEx != null) { + if (lastEx != null) + { throw lastEx; } - return default; + return null; } - public static async Task SendWithTriesAsync(this HttpClient client, Func message, TimeSpan errorTimeout = default) { + public static async Task SendWithTriesAsync(this HttpClient client, Func message, TimeSpan errorTimeout = default) + { Exception lastEx = null; - - for (var i = 0; i < MAX_TRY_COUNT; i++) { - try { + + for (var i = 0; i < MAX_TRY_COUNT; i++) + { + try + { var response = await client.SendAsync(message()); - if (response.StatusCode != HttpStatusCode.OK) { - if (i == MAX_TRY_COUNT - 1) { + if (response.StatusCode != HttpStatusCode.OK) + { + if (i == MAX_TRY_COUNT - 1) + { return response; } @@ -66,61 +77,76 @@ public static async Task SendWithTriesAsync(this HttpClient } return response; - } catch (Exception ex) { + } + catch (Exception ex) + { lastEx = ex; await Task.Delay(GetTimeout(errorTimeout)); } } - if (lastEx != null) { + if (lastEx != null) + { throw lastEx; } - return default; + return null; } - - public static async Task PostWithTriesAsync(this HttpClient client, Uri url, HttpContent content, TimeSpan errorTimeout = default) { + + public static async Task PostWithTriesAsync(this HttpClient client, Uri url, HttpContent content, TimeSpan errorTimeout = default) + { Exception lastEx = null; - - for (var i = 0; i < MAX_TRY_COUNT; i++) { - try { + + for (var i = 0; i < MAX_TRY_COUNT; i++) + { + try + { var response = await client.PostAsync(url, content); - if (response.StatusCode != HttpStatusCode.OK) { + if (response.StatusCode != HttpStatusCode.OK) + { await Task.Delay(GetTimeout(errorTimeout)); - if (i == MAX_TRY_COUNT - 1) { + if (i == MAX_TRY_COUNT - 1) + { return response; } - + continue; } - + return response; - } catch (Exception ex) { + } + catch (Exception ex) + { lastEx = ex; await Task.Delay(GetTimeout(errorTimeout)); } } - if (lastEx != null) { + if (lastEx != null) + { throw lastEx; } - return default; + return null; } - - public static async Task GetHtmlDocWithTriesAsync(this HttpClient client, Uri url, Encoding encoding = null) { - using var response = await client.GetWithTriesAsync(url); + + public static async Task GetHtmlDocWithTriesAsync(this HttpClient client, Uri url, Encoding encoding = null) + { + using var response = await client.GetWithTriesAsync(url); return await response.Content.ReadAsStreamAsync().ContinueWith(t => t.Result.AsHtmlDoc(encoding)); } - - public static async Task GetFromJsonWithTriesAsync(this HttpClient client, Uri url, TimeSpan errorTimeout = default) { + + public static async Task GetFromJsonWithTriesAsync(this HttpClient client, Uri url, TimeSpan errorTimeout = default) + { using var response = await client.GetWithTriesAsync(url, errorTimeout); - if (response == default) { + if (response == null) + { throw new Exception($"Не удалось получить ответ от {url}"); } - if (response.StatusCode != HttpStatusCode.OK) { + if (response.StatusCode != HttpStatusCode.OK) + { var errorBody = await SafeReadAsStringAsync(response.Content); throw new Exception($"Ошибка запроса {url}. Код {(int)response.StatusCode} ({response.StatusCode}).{FormatSnippet(errorBody)}"); } @@ -130,48 +156,65 @@ public static async Task GetFromJsonWithTriesAsync(this HttpClient client, await stream.CopyToAsync(memoryStream); var payload = Encoding.UTF8.GetString(memoryStream.ToArray()); - if (string.IsNullOrWhiteSpace(payload)) { + if (string.IsNullOrWhiteSpace(payload)) + { throw new Exception($"Ответ {url} пустой"); } - if (LooksLikeHtml(payload)) { - throw new Exception($"Сайт вернул HTML вместо JSON по адресу {url}. Возможно сработала защита (DDoS-Guard). Попробуйте увеличить параметры --delay/--timeout или настроить --flare."); + if (LooksLikeHtml(payload)) + { + throw new Exception( + $"Сайт вернул HTML вместо JSON по адресу {url}. Возможно сработала защита (DDoS-Guard). Попробуйте увеличить параметры --delay/--timeout или настроить --flare."); } - try { + try + { return JsonSerializer.Deserialize(payload, new JsonSerializerOptions(JsonSerializerDefaults.Web)) ?? throw new Exception($"Сайт вернул пустой JSON по адресу {url}"); - } catch (JsonException ex) { + } + catch (JsonException ex) + { throw new Exception($"Не удалось разобрать JSON ответа {url}. {ex.Message}. {FormatSnippet(payload)}", ex); } } - public static async Task PostHtmlDocWithTriesAsync(this HttpClient client, Uri url, HttpContent content, Encoding encoding = null) { + public static async Task PostHtmlDocWithTriesAsync(this HttpClient client, Uri url, HttpContent content, Encoding encoding = null) + { using var response = await client.PostWithTriesAsync(url, content); return await response.Content.ReadAsStreamAsync().ContinueWith(t => t.Result.AsHtmlDoc(encoding)); } - private static async Task SafeReadAsStringAsync(HttpContent content) { - try { + private static async Task SafeReadAsStringAsync(HttpContent content) + { + try + { return await content.ReadAsStringAsync(); - } catch { + } + catch + { return string.Empty; } } - private static bool LooksLikeHtml(string value) { + private static bool LooksLikeHtml(string value) + { return value.TrimStart().StartsWith("<", StringComparison.Ordinal); } - private static string FormatSnippet(string value, int maxLength = 300) { - if (string.IsNullOrWhiteSpace(value)) { + private static string FormatSnippet(string value, int maxLength = 300) + { + if (string.IsNullOrWhiteSpace(value)) + { return string.Empty; } var trimmed = value.Trim(); - if (LooksLikeHtml(trimmed)) { + if (LooksLikeHtml(trimmed)) + { return string.Empty; } - if (trimmed.Length > maxLength) { + + if (trimmed.Length > maxLength) + { trimmed = trimmed[..maxLength] + "…"; } diff --git a/Core/Extensions/ImageFormatExtensions.cs b/src/Core/Extensions/ImageFormatExtensions.cs similarity index 77% rename from Core/Extensions/ImageFormatExtensions.cs rename to src/Core/Extensions/ImageFormatExtensions.cs index 75ff446f..0e362ee7 100644 --- a/Core/Extensions/ImageFormatExtensions.cs +++ b/src/Core/Extensions/ImageFormatExtensions.cs @@ -1,12 +1,14 @@ -using System; using EpubSharp; using EpubSharp.Format; -namespace Core.Extensions; +namespace Core.Extensions; -public static class ImageFormatExtensions { - public static EpubContentType ToEpubContentType(this ImageFormat self) { - return self switch { +public static class ImageFormatExtensions +{ + public static EpubContentType ToEpubContentType(this ImageFormat self) + { + return self switch + { ImageFormat.Gif => EpubContentType.ImageGif, ImageFormat.Png => EpubContentType.ImagePng, ImageFormat.Jpeg => EpubContentType.ImageJpeg, @@ -14,4 +16,4 @@ public static EpubContentType ToEpubContentType(this ImageFormat self) { _ => throw new ArgumentOutOfRangeException(nameof(self), self, null) }; } -} \ No newline at end of file +} diff --git a/Core/Extensions/StreamExtension.cs b/src/Core/Extensions/StreamExtension.cs similarity index 67% rename from Core/Extensions/StreamExtension.cs rename to src/Core/Extensions/StreamExtension.cs index 6e7f4ada..bd1b2582 100644 --- a/Core/Extensions/StreamExtension.cs +++ b/src/Core/Extensions/StreamExtension.cs @@ -1,13 +1,14 @@ -using System.IO; using System.Text; using HtmlAgilityPack; -namespace Core.Extensions; +namespace Core.Extensions; -public static class StreamExtension { - public static HtmlDocument AsHtmlDoc(this Stream self, Encoding encoding = null) { +public static class StreamExtension +{ + public static HtmlDocument AsHtmlDoc(this Stream self, Encoding encoding = null) + { var doc = new HtmlDocument(); doc.Load(self, encoding ?? Encoding.UTF8); return doc; } -} \ No newline at end of file +} diff --git a/Core/Extensions/StringBuilderExtensions.cs b/src/Core/Extensions/StringBuilderExtensions.cs similarity index 79% rename from Core/Extensions/StringBuilderExtensions.cs rename to src/Core/Extensions/StringBuilderExtensions.cs index fd6bee8d..ebc9c529 100644 --- a/Core/Extensions/StringBuilderExtensions.cs +++ b/src/Core/Extensions/StringBuilderExtensions.cs @@ -1,15 +1,17 @@ using System.Text; using HtmlAgilityPack; -namespace Core.Extensions; +namespace Core.Extensions; -public static class StringBuilderExtensions { +public static class StringBuilderExtensions +{ /// /// Конвертация строки в Html документ /// /// /// - public static HtmlDocument AsHtmlDoc(this StringBuilder self) { + public static HtmlDocument AsHtmlDoc(this StringBuilder self) + { return self.ToString().AsHtmlDoc(); } -} \ No newline at end of file +} diff --git a/Core/Extensions/StringExtensions.cs b/src/Core/Extensions/StringExtensions.cs similarity index 70% rename from Core/Extensions/StringExtensions.cs rename to src/Core/Extensions/StringExtensions.cs index 5c74f8ab..2f470511 100644 --- a/Core/Extensions/StringExtensions.cs +++ b/src/Core/Extensions/StringExtensions.cs @@ -1,7 +1,4 @@ -using System; -using System.IO; -using System.Linq; -using System.Text; +using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Web; @@ -9,15 +6,17 @@ using AngleSharp.Html.Parser; using HtmlAgilityPack; -namespace Core.Extensions; +namespace Core.Extensions; -public static class StringExtensions { +public static class StringExtensions +{ /// /// Конвертация строки в Html документ /// /// /// - public static HtmlDocument AsHtmlDoc(this string self) { + public static HtmlDocument AsHtmlDoc(this string self) + { var doc = new HtmlDocument(); doc.LoadHtml(self.HtmlDecode()); return doc; @@ -28,14 +27,14 @@ public static HtmlDocument AsHtmlDoc(this string self) { /// /// /// - public static HtmlDocument AsXHtmlDoc(this string self) { + public static HtmlDocument AsXHtmlDoc(this string self) + { HtmlNode.ElementsFlags.Remove("style"); HtmlNode.ElementsFlags.Remove("title"); - var doc = new HtmlDocument { - OptionFixNestedTags = true, - OptionAutoCloseOnEnd = true, - OptionOutputAsXml = true, + var doc = new HtmlDocument + { + OptionFixNestedTags = true, OptionAutoCloseOnEnd = true, OptionOutputAsXml = true, }; doc.LoadHtml(self); @@ -47,9 +46,11 @@ public static HtmlDocument AsXHtmlDoc(this string self) { /// /// /// - public static string RemoveInvalidChars(this string self) { + public static string RemoveInvalidChars(this string self) + { var sb = new StringBuilder(self); - foreach (var invalidFileNameChar in Path.GetInvalidFileNameChars().Union(['"'])) { + foreach (var invalidFileNameChar in Path.GetInvalidFileNameChars().Union(['"'])) + { sb.Replace(invalidFileNameChar, ' '); } @@ -61,17 +62,19 @@ public static string RemoveInvalidChars(this string self) { /// /// /// - public static string CoverQuotes(this string self) { + public static string CoverQuotes(this string self) + { return "\"" + self + "\""; } - + /// /// Обрезка строки /// /// /// /// - public static string Crop(this string self, int lenght) { + public static string Crop(this string self, int lenght) + { return self.Length > lenght ? self[..lenght] : self; } @@ -80,54 +83,65 @@ public static string Crop(this string self, int lenght) { /// /// /// - public static string HtmlDecode(this string self) { - if (string.IsNullOrWhiteSpace(self)) { + public static string HtmlDecode(this string self) + { + if (string.IsNullOrWhiteSpace(self)) + { return self; } - + var temp = HttpUtility.HtmlDecode(self.Replace(">", "").Replace("<", "")); - while (temp != self) { + while (temp != self) + { self = temp; temp = HttpUtility.HtmlDecode(self.Replace(">", "").Replace("<", "")); } - + return self.Trim(); } - public static string CoverTag(this string self, string tag) { + public static string CoverTag(this string self, string tag) + { return string.IsNullOrEmpty(tag) ? self : $"<{tag}>{self}"; } - public static string CleanInvalidXmlChars(this string self) { + public static string CleanInvalidXmlChars(this string self) + { return string.IsNullOrWhiteSpace(self) ? self : Regex.Replace(self, "[\x00-\x08\x0B\x0C\x0E-\x1F\x26]", string.Empty, RegexOptions.Compiled); } - - public static string ReplaceNewLine(this string self) { + + public static string ReplaceNewLine(this string self) + { return string.IsNullOrWhiteSpace(self) ? self : Regex.Replace(self, "\t|\n", " ").CollapseWhitespace().Trim(); } - + /// /// HtmlEncode /// /// /// - public static string HtmlEncode(this string self) { + public static string HtmlEncode(this string self) + { return HttpUtility.HtmlEncode(self.Trim()); } - public static string CollapseWhitespace(this string self) { + public static string CollapseWhitespace(this string self) + { return !string.IsNullOrEmpty(self) ? Regex.Replace(self, @"\s+", " ") : self; } - public static T Deserialize(this string self) { + public static T Deserialize(this string self) + { return JsonSerializer.Deserialize(self); } - public static Uri AsUri(this string self) { + public static Uri AsUri(this string self) + { return new(self); } - public static string PrettyHtml(this string self) { + public static string PrettyHtml(this string self) + { var document = new HtmlParser().ParseDocument(self); using var sw = new StringWriter(); @@ -135,4 +149,4 @@ public static string PrettyHtml(this string self) { return sw.ToString(); } -} \ No newline at end of file +} diff --git a/Core/Extensions/UriExtension.cs b/src/Core/Extensions/UriExtension.cs similarity index 62% rename from Core/Extensions/UriExtension.cs rename to src/Core/Extensions/UriExtension.cs index 04b3a184..2ea2e8d9 100644 --- a/Core/Extensions/UriExtension.cs +++ b/src/Core/Extensions/UriExtension.cs @@ -1,62 +1,74 @@ -using System; using System.Globalization; -using System.Linq; using System.Text.RegularExpressions; using System.Web; -namespace Core.Extensions; +namespace Core.Extensions; -public static class UriExtension { +public static class UriExtension +{ private static readonly IdnMapping Idn = new(); - - public static string GetFileName(this Uri self) { + + public static string GetFileName(this Uri self) + { return self.Segments.Last().Split(":")[0].TrimEnd('/'); } - public static Uri MakeRelativeUri(this Uri self, string relative) { + public static Uri MakeRelativeUri(this Uri self, string relative) + { return new(self, relative); } - public static Uri AppendSegment(this Uri self, string segment) { + public static Uri AppendSegment(this Uri self, string segment) + { return new(self.ToString().TrimEnd('/') + "/" + segment.TrimStart('/')); } - public static string GetSegment(this Uri self, int index) { - var builder = new UriBuilder { - Scheme = self.Scheme, - Host = self.Host, - Path = Regex.Replace(self.AbsolutePath, "/+", "/") + public static string GetSegment(this Uri self, int index) + { + var builder = new UriBuilder + { + Scheme = self.Scheme, Host = self.Host, Path = Regex.Replace(self.AbsolutePath, "/+", "/") }; - + return builder.Uri.Segments[index].Trim('/'); } - public static Uri AppendQueryParameter(this Uri self, string name, object value) { + public static Uri AppendQueryParameter(this Uri self, string name, object value) + { var query = HttpUtility.ParseQueryString(self.Query); query.Add(name, value.ToString()); - return new UriBuilder(self) { + return new UriBuilder(self) + { Query = query.ToString()! }.Uri; } - - public static string GetQueryParameter(this Uri self, string name) { + + public static string GetQueryParameter(this Uri self, string name) + { return HttpUtility.ParseQueryString(self.Query)[name]; } - - public static Uri ReplaceHost(this Uri self, string newHost) { - return new UriBuilder(self) { + + public static Uri ReplaceHost(this Uri self, string newHost) + { + return new UriBuilder(self) + { Host = newHost }.Uri; } - public static bool IsSameHost(this Uri self, Uri url) { + public static bool IsSameHost(this Uri self, Uri url) + { return string.Equals(Idn.GetAscii(self.Host).Replace("www.", ""), Idn.GetAscii(url.Host).Replace("www.", ""), StringComparison.InvariantCultureIgnoreCase); } - - public static bool IsSameSubDomain(this Uri self, Uri url) { + + public static bool IsSameSubDomain(this Uri self, Uri url) + { var urlParts = url.Host.Replace("www.", string.Empty).Split("."); var selfParts = self.Host.Replace("www.", string.Empty).Split("."); - return selfParts.Length >= urlParts.Length && string.Equals(Idn.GetAscii(string.Join(".", selfParts[^urlParts.Length..])), Idn.GetAscii(string.Join(".", urlParts)), StringComparison.InvariantCultureIgnoreCase); + return selfParts.Length >= urlParts.Length && string.Equals( + Idn.GetAscii(string.Join(".", selfParts[^urlParts.Length..])), + Idn.GetAscii(string.Join(".", urlParts)), + StringComparison.InvariantCultureIgnoreCase); } -} \ No newline at end of file +} diff --git a/Core/External/EpubSharp.deps.json b/src/Core/External/EpubSharp.deps.json similarity index 100% rename from Core/External/EpubSharp.deps.json rename to src/Core/External/EpubSharp.deps.json diff --git a/Core/External/EpubSharp.dll b/src/Core/External/EpubSharp.dll similarity index 100% rename from Core/External/EpubSharp.dll rename to src/Core/External/EpubSharp.dll diff --git a/Core/External/EpubSharp.pdb b/src/Core/External/EpubSharp.pdb similarity index 100% rename from Core/External/EpubSharp.pdb rename to src/Core/External/EpubSharp.pdb diff --git a/Core/Logic/BookNameBuilder.cs b/src/Core/Logic/BookNameBuilder.cs similarity index 72% rename from Core/Logic/BookNameBuilder.cs rename to src/Core/Logic/BookNameBuilder.cs index 84cc56c3..72ac8de6 100644 --- a/Core/Logic/BookNameBuilder.cs +++ b/src/Core/Logic/BookNameBuilder.cs @@ -4,8 +4,10 @@ namespace Core.Logic; -public class BookNameBuilder { - public static string Build(string pattern, Book book) { +public class BookNameBuilder +{ + public static string Build(string pattern, Book book) + { var resolver = new InterpolatedStringResolver(StringTokenFormatterSettings.Default); var combinedContainer = resolver .Builder() @@ -13,11 +15,11 @@ public static string Build(string pattern, Book book) { .AddPrefixedObject("Author", book.Author) .AddPrefixedSingle("Seria", "HasSeria", book.Seria is not null); - if (book.Seria is not null) { - combinedContainer.AddPrefixedObject("Seria", book.Seria); + if (book.Seria is not null) + { + combinedContainer.AddPrefixedObject("Seria", book.Seria); } - return resolver.FromContainer(pattern, combinedContainer.CombinedResult()).RemoveInvalidChars(); } -} \ No newline at end of file +} diff --git a/Core/Logic/Builders/AdditionaFileBuilder.cs b/src/Core/Logic/Builders/AdditionaFileBuilder.cs similarity index 80% rename from Core/Logic/Builders/AdditionaFileBuilder.cs rename to src/Core/Logic/Builders/AdditionaFileBuilder.cs index e148250a..1d40fb25 100644 --- a/Core/Logic/Builders/AdditionaFileBuilder.cs +++ b/src/Core/Logic/Builders/AdditionaFileBuilder.cs @@ -1,6 +1,3 @@ -using System.IO; -using System.Linq; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Misc; @@ -9,25 +6,33 @@ namespace Core.Logic.Builders; -public class AdditionaFileBuilder(Options options, ILogger logger) { - private void CreateDirectory(string directory) { - if (!Directory.Exists(directory)) { +public class AdditionaFileBuilder(Options options, ILogger logger) +{ + private void CreateDirectory(string directory) + { + if (!Directory.Exists(directory)) + { Directory.CreateDirectory(directory); } } - public async Task Build(Book book) { + public async Task Build(Book book) + { var additionalPath = BookNameBuilder.Build(options.BookNamePattern, book); - if (!string.IsNullOrWhiteSpace(options.SavePath)) { + if (!string.IsNullOrWhiteSpace(options.SavePath)) + { additionalPath = Path.Combine(options.SavePath, additionalPath); } CreateDirectory(additionalPath); - if (options.HasAdditionalType(AdditionalTypeEnum.Images)) { + if (options.HasAdditionalType(AdditionalTypeEnum.Images)) + { logger.LogInformation("Начинаю сохранение изображений из книги"); - foreach (var chapter in book.Chapters ?? []) { - if (!chapter.Images.Any()) { + foreach (var chapter in book.Chapters ?? []) + { + if (!chapter.Images.Any()) + { logger.LogInformation($"В главе {chapter.Title.CoverQuotes()} нет изображений"); continue; } @@ -37,7 +42,8 @@ public async Task Build(Book book) { logger.LogInformation($"Начинаю сохранение изображений из части {chapter.Title.CoverQuotes()}"); var c = 0; - foreach (var image in chapter.Images) { + foreach (var image in chapter.Images) + { var fileName = Path.Combine(subPath, $"{++c}{Path.GetExtension(image.FullName)}".RemoveInvalidChars()); await File.WriteAllBytesAsync(fileName, image.Content); } @@ -47,7 +53,8 @@ public async Task Build(Book book) { logger.LogInformation("Сохранение изображений из книги завешено"); - if (book.Cover != default) { + if (book.Cover != null) + { var subPath = Path.Combine(additionalPath, AdditionalTypeEnum.Images.ToString()); CreateDirectory(subPath); @@ -56,16 +63,19 @@ public async Task Build(Book book) { } } - if (book.AdditionalFiles.Collection.Count == 0) { + if (book.AdditionalFiles.Collection.Count == 0) + { logger.LogInformation("Нет дополнительных файлов"); return; } - - foreach (var files in book.AdditionalFiles.Collection) { + + foreach (var files in book.AdditionalFiles.Collection) + { var subPath = Path.Combine(additionalPath, files.Key.ToString()); CreateDirectory(subPath); - - foreach (var file in files.Value) { + + foreach (var file in files.Value) + { var fileName = Path.Combine(subPath, file.FullName.RemoveInvalidChars()); logger.LogInformation($"Начинаю сохранение дополнительного файла {fileName.CoverQuotes()}"); await File.WriteAllBytesAsync(fileName, file.Content); @@ -73,4 +83,4 @@ public async Task Build(Book book) { } } } -} \ No newline at end of file +} diff --git a/Core/Logic/Builders/BuilderBase.cs b/src/Core/Logic/Builders/BuilderBase.cs similarity index 75% rename from Core/Logic/Builders/BuilderBase.cs rename to src/Core/Logic/Builders/BuilderBase.cs index 77963238..97fa4355 100644 --- a/Core/Logic/Builders/BuilderBase.cs +++ b/src/Core/Logic/Builders/BuilderBase.cs @@ -1,18 +1,17 @@ -using System.IO; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; using Core.Types.Common; using Microsoft.Extensions.Logging; -namespace Core.Logic.Builders; +namespace Core.Logic.Builders; -public abstract class BuilderBase(Options options, ILogger logger) { +public abstract class BuilderBase(Options options, ILogger logger) +{ protected readonly Options Options = options; protected readonly ILogger Logger = logger; - protected abstract string Extension { get;} + protected abstract string Extension { get; } /// /// Создание файла @@ -26,37 +25,43 @@ public abstract class BuilderBase(Options options, ILogger logger) { /// /// /// - protected virtual string GetFileName(Book book) => $"{GetTitle(book)}.{Extension}"; - + protected virtual string GetFileName(Book book) + => $"{GetTitle(book)}.{Extension}"; + /// /// Получение полного названия книги /// /// /// - protected virtual string GetTitle(Book book) => BookNameBuilder.Build(Options.BookNamePattern, book).Crop(100); + protected virtual string GetTitle(Book book) + => BookNameBuilder.Build(Options.BookNamePattern, book).Crop(100); /// /// Создание файла /// /// Книга - public async Task Build(Book book) { + public async Task Build(Book book) + { var fileName = GetFileName(book); - - if (!string.IsNullOrWhiteSpace(Options.SavePath)) { - if (!Directory.Exists(Options.SavePath)) { + + if (!string.IsNullOrWhiteSpace(Options.SavePath)) + { + if (!Directory.Exists(Options.SavePath)) + { Directory.CreateDirectory(Options.SavePath); } fileName = Path.Combine(Options.SavePath, fileName); } - + Logger.LogInformation($"Начинаю сохранение книги {fileName.CoverQuotes()}"); await BuildInternal(book, fileName); - - if (Options.Cover) { + + if (Options.Cover) + { await SaveCover(Options.SavePath, book.Cover, GetTitle(book)); } - + Logger.LogInformation($"Книга {fileName.CoverQuotes()} успешно сохранена"); } @@ -66,15 +71,19 @@ public async Task Build(Book book) { /// /// /// - private async Task SaveCover(string directory, TempFile cover, string name) { - if (cover == null) { + private async Task SaveCover(string directory, TempFile cover, string name) + { + if (cover == null) + { return; } - + var fileName = $"{name}_cover{cover.Extension}"; - if (!string.IsNullOrWhiteSpace(directory)) { - if (!Directory.Exists(directory)) { + if (!string.IsNullOrWhiteSpace(directory)) + { + if (!Directory.Exists(directory)) + { Directory.CreateDirectory(directory); } diff --git a/Core/Logic/Builders/CbzBuilder.cs b/src/Core/Logic/Builders/CbzBuilder.cs similarity index 70% rename from Core/Logic/Builders/CbzBuilder.cs rename to src/Core/Logic/Builders/CbzBuilder.cs index 190a28a2..e56b3626 100644 --- a/Core/Logic/Builders/CbzBuilder.cs +++ b/src/Core/Logic/Builders/CbzBuilder.cs @@ -1,29 +1,33 @@ -using System.IO; using System.IO.Compression; -using System.Threading.Tasks; using Core.Configs; using Core.Types.Book; using Microsoft.Extensions.Logging; -namespace Core.Logic.Builders; +namespace Core.Logic.Builders; -public class CbzBuilder(Options options, ILogger logger) : BuilderBase(options, logger) { +public class CbzBuilder(Options options, ILogger logger) : BuilderBase(options, logger) +{ protected override string Extension => "cbz"; - protected override async Task BuildInternal(Book book, string fileName) { - if (File.Exists(fileName)) { + protected override async Task BuildInternal(Book book, string fileName) + { + if (File.Exists(fileName)) + { File.Delete(fileName); } - + using var archive = ZipFile.Open(fileName, ZipArchiveMode.Create); - + var c = 0; - foreach (var chapter in book.Chapters) { - if (chapter.Images == default) { + foreach (var chapter in book.Chapters) + { + if (chapter.Images == null) + { continue; } - foreach (var image in chapter.Images) { + foreach (var image in chapter.Images) + { var entry = archive.CreateEntry($"{++c}{image.Extension}", CompressionLevel.Optimal); await using var entryStream = entry.Open(); await using var fileStream = image.GetStream(); @@ -31,4 +35,4 @@ protected override async Task BuildInternal(Book book, string fileName) { } } } -} \ No newline at end of file +} diff --git a/Core/Logic/Builders/EpubBuilder.cs b/src/Core/Logic/Builders/EpubBuilder.cs similarity index 79% rename from Core/Logic/Builders/EpubBuilder.cs rename to src/Core/Logic/Builders/EpubBuilder.cs index 92098435..d45b8bbe 100644 --- a/Core/Logic/Builders/EpubBuilder.cs +++ b/src/Core/Logic/Builders/EpubBuilder.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Core.Configs; +using Core.Configs; using Core.Extensions; using Core.Types.Book; using Core.Types.Common; @@ -11,11 +6,12 @@ using EpubSharp.Format; using Microsoft.Extensions.Logging; -namespace Core.Logic.Builders; +namespace Core.Logic.Builders; -public class EpubBuilder(Options options, ILogger logger) : BuilderBase(options, logger) { +public class EpubBuilder(Options options, ILogger logger) : BuilderBase(options, logger) +{ protected override string Extension => "epub"; - + private readonly EpubWriter _writer = new(); private List Images { get; set; } = new(); @@ -27,7 +23,8 @@ public class EpubBuilder(Options options, ILogger logger) : BuilderBase(options, /// Заголовок части /// Раскодированный текст /// - private static string ApplyPattern(string pattern, string title, string decodeText) { + private static string ApplyPattern(string pattern, string title, string decodeText) + { return pattern.Replace("{title}", title.CleanInvalidXmlChars().ReplaceNewLine()).Replace("{body}", decodeText.HtmlDecode().CleanInvalidXmlChars()).AsXHtmlDoc().AsString(); } @@ -37,69 +34,83 @@ private static string ApplyPattern(string pattern, string title, string decodeTe /// Путь к директории с файлами /// Шаблон поиска файлов /// - private void WithFiles(string directory, string searchPattern) { - foreach (var file in FileProvider.Instance.GetFiles(directory, searchPattern)) { + private void WithFiles(string directory, string searchPattern) + { + foreach (var file in FileProvider.Instance.GetFiles(directory, searchPattern)) + { var fileName = Path.GetFileName(file.Name); - var type = file.Name.EndsWith(".ttf") ? EpubContentType.FontTruetype : + var type = file.Name.EndsWith(".ttf") ? EpubContentType.FontTruetype : file.Name.EndsWith(".css") ? EpubContentType.Css : throw new Exception($"Неизвестный тип файла {fileName}"); - + _writer.AddFile(fileName, file.ReadAllBytes(), type); } } - - private static ImageFormat GetImageFormat(string path) { - if (path.EndsWith(".jpg", StringComparison.InvariantCultureIgnoreCase)) { + + private static ImageFormat GetImageFormat(string path) + { + if (path.EndsWith(".jpg", StringComparison.InvariantCultureIgnoreCase)) + { return ImageFormat.Jpeg; } - if (path.EndsWith(".gif", StringComparison.InvariantCultureIgnoreCase)) { + if (path.EndsWith(".gif", StringComparison.InvariantCultureIgnoreCase)) + { return ImageFormat.Gif; } - if (path.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase)) { + if (path.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase)) + { return ImageFormat.Png; } return path.EndsWith(".svg", StringComparison.InvariantCultureIgnoreCase) ? ImageFormat.Svg : ImageFormat.Jpeg; } - private void Write(Book book) { + private void Write(Book book) + { _writer.AddAuthor(book.Author.Name); - - foreach (var coAuthor in book.CoAuthors) { - _writer.AddAuthor(coAuthor.Name); + + foreach (var coAuthor in book.CoAuthors) + { + _writer.AddAuthor(coAuthor.Name); } - + _writer.SetTitle(book.Title); - - if (book.Cover != null) { + + if (book.Cover != null) + { _writer.SetCover(book.Cover.Content, GetImageFormat(book.Cover.FullName)); } - - if (!string.IsNullOrWhiteSpace(book.Annotation)) { - _writer.AddDescription(book.Annotation.AsHtmlDoc().DocumentNode.GetText()); + + if (!string.IsNullOrWhiteSpace(book.Annotation)) + { + _writer.AddDescription(book.Annotation.AsHtmlDoc().DocumentNode.GetText()); } - - if (book.Seria != default) { + + if (book.Seria != null) + { _writer.AddCollection(book.Seria.Name, book.Seria.Number); } var pattern = FileProvider.Instance.ReadAllText($"{Options.ResourcesPath}/ChapterPattern.xhtml"); - foreach (var chapter in book.Chapters.Where(c => c.IsValid)) { - foreach (var image in chapter.Images) { + foreach (var chapter in book.Chapters.Where(c => c.IsValid)) + { + foreach (var image in chapter.Images) + { _writer.AddFile(image.FullName, Array.Empty(), GetImageFormat(image.FullName).ToEpubContentType()); Images.Add(image); } - + _writer.AddChapter(chapter.Title.HtmlDecode().ReplaceNewLine().RemoveInvalidChars(), ApplyPattern(pattern, chapter.Title, chapter.Content)); } - + WithFiles(Options.ResourcesPath, "*.ttf"); WithFiles(Options.ResourcesPath, "*.css"); } - protected override async Task BuildInternal(Book book, string fileName) { + protected override async Task BuildInternal(Book book, string fileName) + { Write(book); await _writer.Write(fileName, Images.Select(image => new FileMeta(image.FullName, image.FilePath))); } -} \ No newline at end of file +} diff --git a/Core/Logic/Builders/Fb2Builder.cs b/src/Core/Logic/Builders/Fb2Builder.cs similarity index 70% rename from Core/Logic/Builders/Fb2Builder.cs rename to src/Core/Logic/Builders/Fb2Builder.cs index 1fc5a606..baa1ded2 100644 --- a/Core/Logic/Builders/Fb2Builder.cs +++ b/src/Core/Logic/Builders/Fb2Builder.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Text; -using System.Threading; -using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; using Core.Configs; @@ -14,11 +8,12 @@ using HtmlAgilityPack; using Microsoft.Extensions.Logging; -namespace Core.Logic.Builders; +namespace Core.Logic.Builders; -public class Fb2Builder : BuilderBase { +public class Fb2Builder : BuilderBase +{ protected override string Extension => "fb2"; - + private readonly XNamespace _ns = "http://www.gribuser.ru/xml/fictionbook/2.0"; private readonly XNamespace _xlink = "http://www.w3.org/1999/xlink"; @@ -28,42 +23,81 @@ public class Fb2Builder : BuilderBase { private readonly XElement _documentInfo; private readonly List _images = new(); - private readonly Dictionary _map = new() { - {"strong", "strong"}, - {"b", "strong"}, - {"i", "emphasis"}, - {"em", "emphasis"}, - {"emphasis", "emphasis"}, - {"del", "strikethrough"}, - {"strikethrough", "strikethrough"}, - {"blockquote", "cite"}, - {"h1", "subtitle"}, - {"h2", "subtitle"}, - {"h3", "subtitle"}, - {"h4", "subtitle"}, - {"h5", "subtitle"}, - {"h6", "subtitle"}, - {"p", "p"}, - {"div", "p"}, - {"u", "u"}, + private readonly Dictionary _map = new() + { + { + "strong", "strong" + }, + { + "b", "strong" + }, + { + "i", "emphasis" + }, + { + "em", "emphasis" + }, + { + "emphasis", "emphasis" + }, + { + "del", "strikethrough" + }, + { + "strikethrough", "strikethrough" + }, + { + "blockquote", "cite" + }, + { + "h1", "subtitle" + }, + { + "h2", "subtitle" + }, + { + "h3", "subtitle" + }, + { + "h4", "subtitle" + }, + { + "h5", "subtitle" + }, + { + "h6", "subtitle" + }, + { + "p", "p" + }, + { + "div", "p" + }, + { + "u", "u" + }, }; - public Fb2Builder(Options options, ILogger logger) : base(options, logger) { + public Fb2Builder(Options options, ILogger logger) : base(options, logger) + { _description = CreateXElement("description"); _titleInfo = CreateXElement("title-info"); _documentInfo = CreateXElement("document-info"); _body = CreateXElement("body"); } - private XElement CreateXElement(string name) { + private XElement CreateXElement(string name) + { return new XElement(_ns + name); } - private XElement CreateAuthor(Author author) { + private XElement CreateAuthor(Author author) + { var authorElem = CreateXElement("author"); var parts = author.Name.Split(" "); - if (parts.Length == 2) { + if (parts.Length == 2) + { var firstName = CreateXElement("first-name"); firstName.Value = parts[0]; authorElem.Add(firstName); @@ -71,13 +105,16 @@ private XElement CreateAuthor(Author author) { var lastName = CreateXElement("last-name"); lastName.Value = parts[1]; authorElem.Add(lastName); - } else { + } + else + { var firstName = CreateXElement("first-name"); firstName.Value = author.Name; authorElem.Add(firstName); } - if (author.Url != default) { + if (author.Url != null) + { var homePageElem = CreateXElement("home-page"); homePageElem.Value = author.Url.ToString(); authorElem.Add(homePageElem); @@ -86,17 +123,19 @@ private XElement CreateAuthor(Author author) { return authorElem; } - private XElement CreateTitle(string text) { + private XElement CreateTitle(string text) + { var p = CreateXElement("p"); p.Value = text.HtmlDecode().ReplaceNewLine(); - + var title = CreateXElement("title"); - + title.Add(p); return title; } - private static async Task WriteBinary(XmlWriter writer, TempFile tempFile) { + private static async Task WriteBinary(XmlWriter writer, TempFile tempFile) + { const int bufferSize = 1000; var buffer = new byte[bufferSize]; int readBytes; @@ -107,32 +146,36 @@ private static async Task WriteBinary(XmlWriter writer, TempFile tempFile) { await writer.WriteAttributeStringAsync(null, "content-type", null, "image/" + tempFile.Extension.TrimStart('.')); using var br = new BinaryReader(inputFile); - do { + do + { readBytes = br.Read(buffer, 0, bufferSize); await writer.WriteBase64Async(buffer, 0, readBytes); } while (bufferSize <= readBytes); await writer.WriteEndElementAsync(); } - + /// /// Добавление автора книги /// /// Автор /// - private void AddAuthor(Author author) { + private void AddAuthor(Author author) + { var authorElem = CreateAuthor(author); _titleInfo.Add(authorElem); _documentInfo.Add(authorElem); } - + /// /// Добавление со-авторов книги /// /// Со-авторы /// - private void AddCoAuthors(IEnumerable coAuthors) { - foreach (var coAuthor in coAuthors) { + private void AddCoAuthors(IEnumerable coAuthors) + { + foreach (var coAuthor in coAuthors) + { var coAuthorElem = CreateAuthor(coAuthor); _titleInfo.Add(coAuthorElem); _documentInfo.Add(coAuthorElem); @@ -144,7 +187,8 @@ private void AddCoAuthors(IEnumerable coAuthors) { /// /// Название книги /// - private void WithTitle(string title) { + private void WithTitle(string title) + { var bookTitle = CreateXElement("book-title"); bookTitle.Value = title.ReplaceNewLine(); @@ -157,43 +201,52 @@ private void WithTitle(string title) { /// /// Обложка /// - private void WithCover(TempFile cover) { - if (cover != default) { + private void WithCover(TempFile cover) + { + if (cover != null) + { var coverPage = CreateXElement("coverpage"); - + var imageElem = CreateXElement("image"); imageElem.SetAttributeValue(_xlink + "href", "#" + cover.FullName); - + coverPage.Add(imageElem); _titleInfo.Add(coverPage); _images.Add(cover); } } - private void WithBookUrl(Uri url) { - if (url != default) { + private void WithBookUrl(Uri url) + { + if (url != null) + { var srcUrlElem = CreateXElement("src-url"); srcUrlElem.Value = url.ToString().CleanInvalidXmlChars(); _documentInfo.Add(srcUrlElem); } } - private void WithAnnotation(string annotation) { - if (!string.IsNullOrWhiteSpace(annotation)) { + private void WithAnnotation(string annotation) + { + if (!string.IsNullOrWhiteSpace(annotation)) + { _titleInfo.Add(CreateAnnotation(annotation)); } } - private XElement CreateAnnotation(string annotation) { + private XElement CreateAnnotation(string annotation) + { var a = CreateXElement("annotation"); var doc = annotation.AsHtmlDoc(); - foreach (var node in doc.DocumentNode.ChildNodes) { - if (!string.IsNullOrWhiteSpace(node.InnerText)) { + foreach (var node in doc.DocumentNode.ChildNodes) + { + if (!string.IsNullOrWhiteSpace(node.InnerText)) + { ProcessSection(a, node, "p"); } } - + return a; } @@ -202,26 +255,32 @@ private XElement CreateAnnotation(string annotation) { /// /// Список частей /// - private void WithChapters(IEnumerable chapters) { - foreach (var chapter in chapters.Where(c => c.IsValid)) { + private void WithChapters(IEnumerable chapters) + { + foreach (var chapter in chapters.Where(c => c.IsValid)) + { var section = CreateXElement("section"); section.Add(CreateTitle(chapter.Title)); - + var doc = chapter.Content.AsHtmlDoc(); - foreach (var node in doc.DocumentNode.ChildNodes) { + foreach (var node in doc.DocumentNode.ChildNodes) + { ProcessSection(section, node); } - + _body.Add(section); - foreach (var image in chapter.Images) { + foreach (var image in chapter.Images) + { _images.Add(image); } } } - private void WithSeria(Seria seria) { - if (seria != default) { + private void WithSeria(Seria seria) + { + if (seria != null) + { var sequenceElem = CreateXElement("sequence"); sequenceElem.SetAttributeValue("name", seria.Name.CleanInvalidXmlChars()); sequenceElem.SetAttributeValue("number", seria.Number.CleanInvalidXmlChars()); @@ -229,70 +288,90 @@ private void WithSeria(Seria seria) { } } - private void WithLang(string lang) { - if (!string.IsNullOrWhiteSpace(lang)) { + private void WithLang(string lang) + { + if (!string.IsNullOrWhiteSpace(lang)) + { var langElem = CreateXElement("lang"); langElem.Value = lang; _titleInfo.Add(langElem); } } - private static bool IsTextNode(HtmlNode node) { + private static bool IsTextNode(HtmlNode node) + { return node.Name is "#text" or "span"; } - private void ProcessSection(XElement parent, HtmlNode node, string textNode = "") { - if (node.Name == "br") { + private void ProcessSection(XElement parent, HtmlNode node, string textNode = "") + { + if (node.Name == "br") + { parent.Add(CreateXElement("br")); return; } - - if (node.Name == "a") { + + if (node.Name == "a") + { var href = node.Attributes["href"]?.Value ?? string.Empty; - if (string.IsNullOrWhiteSpace(href)) { - if (string.IsNullOrWhiteSpace(textNode)) { + if (string.IsNullOrWhiteSpace(href)) + { + if (string.IsNullOrWhiteSpace(textNode)) + { parent.Add(node.InnerText); - } else { + } + else + { var tag = CreateXElement(textNode); tag.Value = node.InnerText; parent.Add(tag); } - } else { + } + else + { var a = CreateXElement("a"); a.SetAttributeValue(_xlink + "href", href); a.Value = node.InnerText; - - if (string.IsNullOrWhiteSpace(textNode)) { + + if (string.IsNullOrWhiteSpace(textNode)) + { parent.Add(a); - } else { + } + else + { var tag = CreateXElement(textNode); tag.Add(a); parent.Add(tag); } } - + return; } - - if (node.ChildNodes.Count > 0) { + + if (node.ChildNodes.Count > 0) + { var section = CreateXElement(_map.GetValueOrDefault(node.Name, "p")); - foreach (var child in node.ChildNodes) { + foreach (var child in node.ChildNodes) + { ProcessSection(IsTextNode(node) ? parent : section, child); } - if (!section.IsEmpty) { + if (!section.IsEmpty) + { parent.Add(section); } return; } - - if (node.Name == "img") { - if (node.Attributes["src"] == null) { + + if (node.Name == "img") + { + if (node.Attributes["src"] == null) + { return; } - + var imageElem = CreateXElement("image"); imageElem.SetAttributeValue(_xlink + "href", "#" + node.Attributes["src"].Value); parent.Add(imageElem); @@ -301,50 +380,60 @@ private void ProcessSection(XElement parent, HtmlNode node, string textNode = "" } var nodeText = node.InnerText.HtmlDecode().CleanInvalidXmlChars(); - if (node.InnerText.StartsWith(" ")) { + if (node.InnerText.StartsWith(" ")) + { nodeText = " " + nodeText; } - - if (node.InnerText.EndsWith(" ")) { + + if (node.InnerText.EndsWith(" ")) + { nodeText += " "; } - - if (IsTextNode(node)) { - if (string.IsNullOrWhiteSpace(textNode)) { + + if (IsTextNode(node)) + { + if (string.IsNullOrWhiteSpace(textNode)) + { parent.Add(new XText(nodeText)); - } else { + } + else + { var tag = CreateXElement(textNode); tag.Value = nodeText; parent.Add(tag); } - + return; } - if (_map.TryGetValue(node.Name, out var fb2Tag)) { + if (_map.TryGetValue(node.Name, out var fb2Tag)) + { var tag = CreateXElement(fb2Tag); tag.Value = nodeText; parent.Add(tag); - } else { + } + else + { parent.Add(nodeText); Logger.LogInformation(node.Name); } } - private XElement GetDateElement(DateTime date) { + private XElement GetDateElement(DateTime date) + { var today = date.ToString("yyyy-MM-dd"); - + var dateElem = CreateXElement("date"); dateElem.SetAttributeValue("value", today); dateElem.Value = today; - + return dateElem; } - - protected override async Task BuildInternal(Book book, string fileName) { + protected override async Task BuildInternal(Book book, string fileName) + { await using var file = File.Create(fileName); - + AddAuthor(book.Author); AddCoAuthors(book.CoAuthors); WithBookUrl(book.Url); @@ -354,17 +443,18 @@ protected override async Task BuildInternal(Book book, string fileName) { WithLang(book.Lang); WithSeria(book.Seria); WithChapters(book.Chapters); - + _documentInfo.Add(GetDateElement(DateTime.Today)); - + var programUsed = CreateXElement("program-used"); programUsed.Value = "Elib2Ebook"; _documentInfo.Add(programUsed); - + _description.Add(_titleInfo); _description.Add(_documentInfo); - - var xws = new XmlWriterSettings { + + var xws = new XmlWriterSettings + { Async = true, Encoding = new UTF8Encoding(false), Indent = true, @@ -373,18 +463,19 @@ protected override async Task BuildInternal(Book book, string fileName) { }; await using var writer = XmlWriter.Create(file, xws); - var cancellationToken = new CancellationToken(); - + var cancellationToken = CancellationToken.None; + await writer.WriteStartElementAsync(string.Empty, "FictionBook", _ns.NamespaceName); await writer.WriteAttributeStringAsync("xmlns", "l", null, _xlink.NamespaceName); await _description.WriteToAsync(writer, cancellationToken); await _body.WriteToAsync(writer, cancellationToken); - foreach (var image in _images) { + foreach (var image in _images) + { await WriteBinary(writer, image); } await writer.WriteEndElementAsync(); await writer.FlushAsync(); } -} \ No newline at end of file +} diff --git a/Core/Logic/Builders/JsonBuilder.cs b/src/Core/Logic/Builders/JsonBuilder.cs similarity index 69% rename from Core/Logic/Builders/JsonBuilder.cs rename to src/Core/Logic/Builders/JsonBuilder.cs index 99a97f80..3093d966 100644 --- a/Core/Logic/Builders/JsonBuilder.cs +++ b/src/Core/Logic/Builders/JsonBuilder.cs @@ -1,22 +1,23 @@ -using System.IO; using System.Text.Json; -using System.Threading.Tasks; using Core.Configs; using Core.Types.Book; using Microsoft.Extensions.Logging; -namespace Core.Logic.Builders; +namespace Core.Logic.Builders; -public class JsonBuilder(Options options, ILogger logger) : BuilderBase(options, logger) { +public class JsonBuilder(Options options, ILogger logger) : BuilderBase(options, logger) +{ protected override string Extension => "json"; - protected override async Task BuildInternal(Book book, string fileName) { + protected override async Task BuildInternal(Book book, string fileName) + { await using var file = File.Create(fileName); - - var jsonSerializerOptions = new JsonSerializerOptions { + + var jsonSerializerOptions = new JsonSerializerOptions + { WriteIndented = true }; - + await JsonSerializer.SerializeAsync(file, book, jsonSerializerOptions); } -} \ No newline at end of file +} diff --git a/Core/Logic/Builders/JsonLiteBuilder.cs b/src/Core/Logic/Builders/JsonLiteBuilder.cs similarity index 72% rename from Core/Logic/Builders/JsonLiteBuilder.cs rename to src/Core/Logic/Builders/JsonLiteBuilder.cs index 258635f4..ecd030b0 100644 --- a/Core/Logic/Builders/JsonLiteBuilder.cs +++ b/src/Core/Logic/Builders/JsonLiteBuilder.cs @@ -1,8 +1,5 @@ -using System; -using System.IO; using System.Text.Json; using System.Text.Json.Serialization; -using System.Threading.Tasks; using Core.Configs; using Core.Types.Book; using Core.Types.Common; @@ -10,15 +7,19 @@ namespace Core.Logic.Builders; -public class ShortImageConverter : JsonConverter { - public override TempFile Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { +public class ShortImageConverter : JsonConverter +{ + public override TempFile Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { return JsonSerializer.Deserialize(ref reader, options); } - public override void Write(Utf8JsonWriter writer, TempFile value, JsonSerializerOptions options) { + public override void Write(Utf8JsonWriter writer, TempFile value, JsonSerializerOptions options) + { writer.WriteStartObject(); - - if (value.Url != default) { + + if (value.Url != null) + { writer.WriteString(nameof(value.Url), value.Url.ToString()); } @@ -30,38 +31,48 @@ public override void Write(Utf8JsonWriter writer, TempFile value, JsonSerializer } } -public class ShortChapterConverter : JsonConverter { - public override Chapter Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { +public class ShortChapterConverter : JsonConverter +{ + public override Chapter Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { return JsonSerializer.Deserialize(ref reader, options); } - public override void Write(Utf8JsonWriter writer, Chapter value, JsonSerializerOptions options) { + public override void Write(Utf8JsonWriter writer, Chapter value, JsonSerializerOptions options) + { writer.WriteStartObject(); - + writer.WriteString(nameof(value.Title), value.Title); writer.WriteBoolean(nameof(value.IsValid), value.IsValid); - + writer.WriteStartArray(nameof(value.Images)); - foreach (var image in value.Images) { + foreach (var image in value.Images) + { JsonSerializer.Serialize(writer, image, options); } - + writer.WriteEndArray(); writer.WriteEndObject(); } } -public class JsonLiteBuilder(Options options, ILogger logger) : BuilderBase(options, logger) { +public class JsonLiteBuilder(Options options, ILogger logger) : BuilderBase(options, logger) +{ protected override string Extension => "json"; - protected override async Task BuildInternal(Book book, string fileName) { + protected override async Task BuildInternal(Book book, string fileName) + { await using var file = File.Create(fileName); - - var jsonSerializerOptions = new JsonSerializerOptions { + + var jsonSerializerOptions = new JsonSerializerOptions + { WriteIndented = true, - Converters = { new ShortChapterConverter(), new ShortImageConverter() } + Converters = + { + new ShortChapterConverter(), new ShortImageConverter() + } }; await JsonSerializer.SerializeAsync(file, book, jsonSerializerOptions); } -} \ No newline at end of file +} diff --git a/Core/Logic/Builders/TxtBuilder.cs b/src/Core/Logic/Builders/TxtBuilder.cs similarity index 68% rename from Core/Logic/Builders/TxtBuilder.cs rename to src/Core/Logic/Builders/TxtBuilder.cs index e7ae5707..095c3146 100644 --- a/Core/Logic/Builders/TxtBuilder.cs +++ b/src/Core/Logic/Builders/TxtBuilder.cs @@ -1,44 +1,48 @@ -using System.IO; -using System.Linq; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; using Microsoft.Extensions.Logging; -namespace Core.Logic.Builders; +namespace Core.Logic.Builders; -public class TxtBuilder(Options options, ILogger logger) : BuilderBase(options, logger) { +public class TxtBuilder(Options options, ILogger logger) : BuilderBase(options, logger) +{ protected override string Extension => "txt"; - protected override async Task BuildInternal(Book book, string fileName) { - if (File.Exists(fileName)) { + protected override async Task BuildInternal(Book book, string fileName) + { + if (File.Exists(fileName)) + { File.Delete(fileName); } - + await using var file = File.CreateText(fileName); - - foreach (var chapter in book.Chapters.Where(c => c.IsValid)) { + + foreach (var chapter in book.Chapters.Where(c => c.IsValid)) + { await file.WriteLineAsync(" " + chapter.Title); await file.WriteLineAsync(); var prettyText = chapter.Content.PrettyHtml().AsHtmlDoc().DocumentNode.GetText(); - using var sr = new StringReader(prettyText); - - while (true) { + using var sr = new StringReader(prettyText); + + while (true) + { var line = await sr.ReadLineAsync(); - if (line == null) { + if (line == null) + { break; } - if (string.IsNullOrWhiteSpace(line)) { + if (string.IsNullOrWhiteSpace(line)) + { continue; } - + await file.WriteLineAsync(" " + line.Trim()); } - + await file.WriteLineAsync(); } } -} \ No newline at end of file +} diff --git a/Core/Logic/FileProvider.cs b/src/Core/Logic/FileProvider.cs similarity index 71% rename from Core/Logic/FileProvider.cs rename to src/Core/Logic/FileProvider.cs index 0acf1020..da6f06bc 100644 --- a/Core/Logic/FileProvider.cs +++ b/src/Core/Logic/FileProvider.cs @@ -1,20 +1,20 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; +using System.Reflection; using Microsoft.Extensions.FileProviders; namespace Core.Logic; -public class FileProvider { +public class FileProvider +{ public static readonly IFileProvider Instance; - static FileProvider() { + static FileProvider() + { var cwd = AppDomain.CurrentDomain.BaseDirectory; var providers = new List(); var patternsDirectory = Path.Combine(cwd); - if (Directory.Exists(patternsDirectory)) { + if (Directory.Exists(patternsDirectory)) + { providers.Add(new PhysicalFileProvider(patternsDirectory)); } @@ -22,4 +22,4 @@ static FileProvider() { Instance = new CompositeFileProvider(providers); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/AcomicsGetter.cs b/src/Core/Logic/Getters/AcomicsGetter.cs similarity index 73% rename from Core/Logic/Getters/AcomicsGetter.cs rename to src/Core/Logic/Getters/AcomicsGetter.cs index 9755b78c..4d8b2a6a 100644 --- a/Core/Logic/Getters/AcomicsGetter.cs +++ b/src/Core/Logic/Getters/AcomicsGetter.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -11,26 +7,31 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class AcomicsGetter(BookGetterConfig config) : GetterBase(config) { +public class AcomicsGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://acomics.ru"); - protected override string GetId(Uri url) => url.GetSegment(1); + protected override string GetId(Uri url) + => url.GetSegment(1); - public override async Task Init() { + public override async Task Init() + { await base.Init(); Config.CookieContainer.Add(SystemUrl, new Cookie("ageRestrict", "18")); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var id = GetId(url); url = SystemUrl.MakeRelativeUri(id); var doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri($"{id}/1")); var title = doc.QuerySelector("meta[property=og:title]").Attributes["content"].Value; - var book = new Book(url) { + var book = new Book(url) + { Chapters = await FillChapters(doc, id, title, url), Title = title, Author = GetAuthor(doc, url), @@ -38,30 +39,35 @@ public override async Task Get(Uri url) { }; book.Cover = book.Chapters.FirstOrDefault()?.Images.FirstOrDefault(); - + return book; } - private static Author GetAuthor(HtmlDocument doc, Uri url) { + private static Author GetAuthor(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("article.authors,a.username"); return new Author(a.GetText(), url.MakeRelativeUri(a.Attributes["href"].Value)); } - private async Task> FillChapters(HtmlDocument doc, string bookId, string title, Uri url) { - if (Config.Options.NoChapters) { + private async Task> FillChapters(HtmlDocument doc, string bookId, string title, Uri url) + { + if (Config.Options.NoChapters) + { return []; } - - var chapter = new Chapter { + + var chapter = new Chapter + { Title = title }; - + var pages = int.Parse(doc.GetTextBySelector("span.issueNumber,span.number-without-name,span.number-with-name").Split("/").Last()); var sb = new StringBuilder(); - for (var i = 1; i <= pages; i++) { + for (var i = 1; i <= pages; i++) + { Config.Logger.LogInformation($"Получаю страницу {i}/{pages}"); var response = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri($"{bookId}/{i}")); - var img = response.QuerySelector("#mainImage")??response.QuerySelector("img.issue"); + var img = response.QuerySelector("#mainImage") ?? response.QuerySelector("img.issue"); var src = url.MakeRelativeUri(img.Attributes["src"].Value); sb.Append($""); } @@ -70,6 +76,9 @@ private async Task> FillChapters(HtmlDocument doc, string b chapter.Images = await GetImages(chapterDoc, url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; - return new [] { chapter }; + return new[] + { + chapter + }; } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/AuthorTodayGetter.cs b/src/Core/Logic/Getters/AuthorTodayGetter.cs similarity index 74% rename from Core/Logic/Getters/AuthorTodayGetter.cs rename to src/Core/Logic/Getters/AuthorTodayGetter.cs index 8eb6c4d1..73549b3f 100644 --- a/Core/Logic/Getters/AuthorTodayGetter.cs +++ b/src/Core/Logic/Getters/AuthorTodayGetter.cs @@ -1,13 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Net.Http; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Security.Cryptography; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Misc; @@ -18,11 +13,12 @@ namespace Core.Logic.Getters; -public class AuthorTodayGetter(BookGetterConfig config) : GetterBase(config) { +public class AuthorTodayGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://author.today/"); - + private Uri _apiUrl => new("https://api.author.today/"); - + /// /// IP сайта author.today /// @@ -32,7 +28,7 @@ public class AuthorTodayGetter(BookGetterConfig config) : GetterBase(config) { /// IP сайта api.author.today /// private Uri _apiIp => new("https://185.26.98.195/"); - + private bool _bypass; private Uri ApiUrl => _bypass ? _apiIp : _apiUrl; @@ -41,54 +37,73 @@ public class AuthorTodayGetter(BookGetterConfig config) : GetterBase(config) { private string UserId { get; set; } = string.Empty; - protected override string GetId(Uri url) => url.GetSegment(2); + protected override string GetId(Uri url) + => url.GetSegment(2); private const string AT_CERT = "rmy8LDkVZR+pSXopxUte7hFbA6I="; private string hashedCert => Convert.ToHexString(SHA1.HashData(Encoding.UTF8.GetBytes(AT_CERT))).ToUpper(); - public override async Task Init() { + public override async Task Init() + { Config.Client.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); Config.Client.DefaultRequestHeaders.Add("Accept-Language", "ru"); Config.Client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate, br"); Config.Client.DefaultRequestHeaders.Add("User-Agent", "okhttp/4.12.0 X_AT_API"); Config.Client.DefaultRequestHeaders.Add("X-AT-Client", "android_1.8.013-GMS"); Config.Client.DefaultRequestHeaders.Add("X-AT-Certificate", hashedCert); - + var response = await Config.Client.GetAsync(_apiUrl); _bypass = response.StatusCode != HttpStatusCode.OK; - Config.Logger.LogInformation(_bypass ? - $"Сайт {_apiUrl} не доступен. Работаю через {_apiIp}" : - $"Сайт {_apiUrl} доступен. Работаю через него"); + Config.Logger.LogInformation( + _bypass ? + $"Сайт {_apiUrl} не доступен. Работаю через {_apiIp}" : + $"Сайт {_apiUrl} доступен. Работаю через него"); Config.Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "guest"); } - - public override async Task Authorize() { - if (!Config.HasCredentials) { + + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } - - var response = await Config.Client.SendAsync(GetDefaultMessage(ApiUrl.MakeRelativeUri("/v1/account/login-by-password"), _apiUrl, JsonContent.Create(new { Config.Options.Login, Config.Options.Password }))); + + var response = await Config.Client.SendAsync( + GetDefaultMessage( + ApiUrl.MakeRelativeUri("/v1/account/login-by-password"), + _apiUrl, + JsonContent.Create( + new + { + Config.Options.Login, Config.Options.Password + }))); + var data = await response.Content.ReadFromJsonAsync(); - if (!string.IsNullOrWhiteSpace(data?.Token)) { + if (!string.IsNullOrWhiteSpace(data?.Token)) + { Config.Logger.LogInformation("Успешно авторизовались"); Config.Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", data.Token); response = await Config.Client.SendWithTriesAsync(() => GetDefaultMessage(ApiUrl.MakeRelativeUri("/v1/account/current-user"), _apiUrl)); var user = await response.Content.ReadFromJsonAsync(); UserId = user!.Id.ToString(); - } else { + } + else + { throw new Exception($"Не удалось авторизоваться. {data?.Message}"); } } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri($"/work/{GetId(url)}"); var details = await GetBookDetails(GetId(url)); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(details), Chapters = await FillChapters(details), Title = details.Title, @@ -103,24 +118,30 @@ public override async Task Get(Uri url) { return book; } - private async Task FillAdditional(Book book, AuthorTodayBookDetails details) { - if (!Config.Options.HasAdditionalType(AdditionalTypeEnum.Images) || details.GalleryImages == default || details.GalleryImages.Length == 0) { + private async Task FillAdditional(Book book, AuthorTodayBookDetails details) + { + if (!Config.Options.HasAdditionalType(AdditionalTypeEnum.Images) || details.GalleryImages == null || details.GalleryImages.Length == 0) + { return; } Config.Logger.LogInformation("Загружаю дополнительные иллюстрации"); - foreach (var image in details.GalleryImages) { + foreach (var image in details.GalleryImages) + { book.AdditionalFiles.Add(AdditionalTypeEnum.Images, await SaveImage(SystemUrl.MakeRelativeUri(image.Url))); } + Config.Logger.LogInformation("Дополнительные иллюстрации загружены"); } - private HttpRequestMessage GetDefaultMessage(Uri uri, Uri host, HttpContent content = null) { - var message = new HttpRequestMessage(content == default ? HttpMethod.Get : HttpMethod.Post, uri); + private HttpRequestMessage GetDefaultMessage(Uri uri, Uri host, HttpContent content = null) + { + var message = new HttpRequestMessage(content == null ? HttpMethod.Get : HttpMethod.Post, uri); message.Content = content; message.Version = Config.Client.DefaultRequestVersion; - - foreach (var header in Config.Client.DefaultRequestHeaders) { + + foreach (var header in Config.Client.DefaultRequestHeaders) + { message.Headers.Add(header.Key, header.Value); } @@ -129,67 +150,83 @@ private HttpRequestMessage GetDefaultMessage(Uri uri, Uri host, HttpContent cont return message; } - private async Task GetBookDetails(string bookId) { + private async Task GetBookDetails(string bookId) + { var response = await Config.Client.SendWithTriesAsync(() => GetDefaultMessage(ApiUrl.MakeRelativeUri($"/v1/work/{bookId}/details"), _apiUrl)); - if (response.StatusCode != HttpStatusCode.OK) { + if (response.StatusCode != HttpStatusCode.OK) + { throw new Exception("Книга не найдена"); } return await response.Content.ReadFromJsonAsync(); } - private Author GetAuthor(AuthorTodayBookDetails book) { + private Author GetAuthor(AuthorTodayBookDetails book) + { return new Author(book.AuthorFio, SystemUrl.MakeRelativeUri($"/u/{book.AuthorUserName}/works")); } - - private IEnumerable GetCoAuthors(AuthorTodayBookDetails book) { + + private IEnumerable GetCoAuthors(AuthorTodayBookDetails book) + { var result = new List(); - if (!string.IsNullOrWhiteSpace(book.CoAuthorFio)) { - result.Add( new Author(book.CoAuthorFio, SystemUrl.MakeRelativeUri($"/u/{book.CoAuthorUserName}/works"))); + if (!string.IsNullOrWhiteSpace(book.CoAuthorFio)) + { + result.Add(new Author(book.CoAuthorFio, SystemUrl.MakeRelativeUri($"/u/{book.CoAuthorUserName}/works"))); } return result; } - private Seria GetSeria(AuthorTodayBookDetails book) { - if (!book.SeriesId.HasValue) { - return default; + private Seria GetSeria(AuthorTodayBookDetails book) + { + if (!book.SeriesId.HasValue) + { + return null; } - return new Seria { + return new Seria + { Name = book.SeriesTitle.Trim(), Number = book.SeriesWorkNumber.HasValue ? book.SeriesWorkNumber.ToString() : string.Empty, Url = SystemUrl.MakeRelativeUri($"/work/series/{book.SeriesId}") }; } - private Task GetCover(AuthorTodayBookDetails book) { + private Task GetCover(AuthorTodayBookDetails book) + { return !string.IsNullOrWhiteSpace(book.CoverUrl) ? SaveImage(book.CoverUrl.AsUri()) : Task.FromResult(default(TempFile)); } - protected override HttpRequestMessage GetImageRequestMessage(Uri uri) { - if (uri.IsSameHost(SystemUrl) || uri.IsSameSubDomain(SystemUrl)) { + protected override HttpRequestMessage GetImageRequestMessage(Uri uri) + { + if (uri.IsSameHost(SystemUrl) || uri.IsSameSubDomain(SystemUrl)) + { return GetDefaultMessage(SiteUrl.MakeRelativeUri(uri.AbsolutePath), uri); } return base.GetImageRequestMessage(uri); } - private async Task> FillChapters(AuthorTodayBookDetails book) { + private async Task> FillChapters(AuthorTodayBookDetails book) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - - foreach (var atChapter in await GetChapters(book)) { + + foreach (var atChapter in await GetChapters(book)) + { var title = atChapter.Title.ReplaceNewLine(); Config.Logger.LogInformation($"Загружаю главу {title.CoverQuotes()}"); - - var chapter = new Chapter { + + var chapter = new Chapter + { Title = title }; - if (atChapter.IsSuccessful) { + if (atChapter.IsSuccessful) + { var chapterDoc = atChapter.Decode(UserId, AT_CERT).AsHtmlDoc(); chapter.Images = await GetImages(chapterDoc, SystemUrl); chapter.Content = chapterDoc.DocumentNode.InnerHtml; @@ -197,23 +234,26 @@ private async Task> FillChapters(AuthorTodayBookDetails boo result.Add(chapter); } - + return result; } - private async Task> GetChapters(AuthorTodayBookDetails book) { + private async Task> GetChapters(AuthorTodayBookDetails book) + { var result = new List(); - - foreach (var chunk in book.Chapters.Where(c => !c.IsDraft).OrderBy(c => c.SortOrder).Chunk(100)) { + + foreach (var chunk in book.Chapters.Where(c => !c.IsDraft).OrderBy(c => c.SortOrder).Chunk(100)) + { var ids = string.Join("&", chunk.Select((c, i) => $"ids[{i}]={c.Id}")); var uri = ApiUrl.MakeRelativeUri($"/v1/work/{book.Id}/chapter/many-texts?{ids}"); var response = await Config.Client.SendWithTriesAsync(() => GetDefaultMessage(uri, _apiUrl)); var chapters = await response.Content.ReadFromJsonAsync(); - if (chapters != default) { + if (chapters != null) + { result.AddRange(chapters.Where(c => c.Code != "NotFound")); } } return SliceToc(result, c => c.Title); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/BookInBookGetter.cs b/src/Core/Logic/Getters/BookInBookGetter.cs similarity index 72% rename from Core/Logic/Getters/BookInBookGetter.cs rename to src/Core/Logic/Getters/BookInBookGetter.cs index a12a0ed2..aa101f7e 100644 --- a/Core/Logic/Getters/BookInBookGetter.cs +++ b/src/Core/Logic/Getters/BookInBookGetter.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -12,18 +7,22 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class BookInBookGetter(BookGetterConfig config) : GetterBase(config) { +public class BookInBookGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://bookinbook.ru/"); - protected override string GetId(Uri url) => url.GetQueryParameter("id"); + protected override string GetId(Uri url) + => url.GetQueryParameter("id"); - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri($"book?id={GetId(url)}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - - var book = new Book(url) { + + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("span.main-info__name"), @@ -31,20 +30,23 @@ public override async Task Get(Uri url) { Annotation = doc.QuerySelector("div.annotation-form__text")?.InnerHtml, Seria = GetSeria(doc) }; - + return book; } - - private async Task> FillChapters(HtmlDocument doc, Uri uri) { + + private async Task> FillChapters(HtmlDocument doc, Uri uri) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var bookChapter in GetToc(doc, uri)) { + foreach (var bookChapter in GetToc(doc, uri)) + { var chapter = new Chapter(); Config.Logger.LogInformation($"Загружаю главу {bookChapter.Title.CoverQuotes()}"); - + var chapterDoc = await GetChapter(bookChapter.Url); chapter.Title = bookChapter.Title; @@ -56,59 +58,73 @@ private async Task> FillChapters(HtmlDocument doc, Uri uri) return result; } - - private async Task GetChapter(Uri url) { + + private async Task GetChapter(Uri url) + { var sb = new StringBuilder(); - - for (var i = 1; ; i++) { + + for (var i = 1;; i++) + { var uri = SystemUrl.MakeRelativeUri($"read?id={url.GetQueryParameter("id")}&chapter={url.GetQueryParameter("chapter")}&page={i}"); using var response = await Config.Client.GetWithTriesAsync(uri, TimeSpan.FromMilliseconds(100)); - if (response == default) { + if (response == null) + { return sb.AsHtmlDoc(); } var doc = await response.Content.ReadAsStreamAsync().ContinueWith(t => t.Result.AsHtmlDoc()); var text = doc.GetTextBySelector("#PAGE_TEXT").Deserialize().HtmlDecode(); using var sr = new StringReader(text); - - while (true) { + + while (true) + { var line = await sr.ReadLineAsync(); - if (line == null) { + if (line == null) + { break; } - if (string.IsNullOrWhiteSpace(line)) { + if (string.IsNullOrWhiteSpace(line)) + { continue; } - + sb.Append(line.CoverTag("p")); } } } - private IEnumerable GetToc(HtmlDocument doc, Uri url) { - var urlChapters = doc.QuerySelectorAll("a.chapters-form__chapter").Select(a => new UrlChapter(url.MakeRelativeUri(a.Attributes["href"].Value), a.GetText().ReplaceNewLine())).ToList(); + private IEnumerable GetToc(HtmlDocument doc, Uri url) + { + var urlChapters = doc.QuerySelectorAll("a.chapters-form__chapter").Select(a => new UrlChapter(url.MakeRelativeUri(a.Attributes["href"].Value), a.GetText().ReplaceNewLine())) + .ToList(); + return SliceToc(urlChapters, c => c.Title); } - - private static Seria GetSeria(HtmlDocument doc) { + + private static Seria GetSeria(HtmlDocument doc) + { var a = doc.QuerySelector("span.series-info__name"); - if (a != default) { - return new Seria { + if (a != null) + { + return new Seria + { Name = a.GetText() }; } - return default; + return null; } - - private Task GetCover(HtmlDocument doc, Uri uri) { + + private Task GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("img.book-cover__image")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - - private static Author GetAuthor(HtmlDocument doc, Uri uri) { + + private static Author GetAuthor(HtmlDocument doc, Uri uri) + { var a = doc.QuerySelector("a.author"); return new Author(a.GetText(), uri.MakeRelativeUri(a.Attributes["href"].Value)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/BookRiverGetter.cs b/src/Core/Logic/Getters/BookRiverGetter.cs similarity index 73% rename from Core/Logic/Getters/BookRiverGetter.cs rename to src/Core/Logic/Getters/BookRiverGetter.cs index dbe1b8af..78a986f5 100644 --- a/Core/Logic/Getters/BookRiverGetter.cs +++ b/src/Core/Logic/Getters/BookRiverGetter.cs @@ -1,11 +1,7 @@ -using System; -using System.Collections.Generic; using System.Net; -using System.Net.Http; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -15,45 +11,53 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class BookriverGetter(BookGetterConfig config) : GetterBase(config) { +public class BookriverGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://bookriver.ru/"); private Uri _apiUrl => new($"https://api.{SystemUrl.Host}/"); private string _token; - - protected override string GetId(Uri url) => url.GetSegment(2); - public override async Task Authorize() { - if (!Config.HasCredentials) { + protected override string GetId(Uri url) + => url.GetSegment(2); + + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } - var payload = new { - email = Config.Options.Login, - password = Config.Options.Password, - rememberMe = 1 + var payload = new + { + email = Config.Options.Login, password = Config.Options.Password, rememberMe = 1 }; using var post = await Config.Client.PostAsJsonAsync(_apiUrl.MakeRelativeUri("/api/v1/auth/login"), payload); var data = await post.Content.ReadFromJsonAsync(); - if (!string.IsNullOrWhiteSpace(data.Token)) { + if (!string.IsNullOrWhiteSpace(data.Token)) + { Config.Logger.LogInformation("Успешно авторизовались"); _token = data.Token; Config.CookieContainer.Add(new Cookie("authToken", _token, "/", SystemUrl.Host)); - } else { + } + else + { throw new Exception("Не удалось авторизоваться"); } } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var bookId = GetId(url); url = SystemUrl.MakeRelativeUri($"/book/{bookId}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(url, bookId), Title = doc.GetTextBySelector("h1[itemprop=name]"), @@ -61,79 +65,92 @@ public override async Task Get(Uri url) { Annotation = doc.QuerySelector("span[itemprop=description]")?.InnerHtml, Seria = GetSeria(doc, url) }; - + return book; } - private static Author GetAuthor(HtmlDocument doc, Uri url) { + private static Author GetAuthor(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("span[itemprop=author] a"); return new Author(a.GetText(), url.MakeRelativeUri(a.Attributes["href"].Value)); } - private static Seria GetSeria(HtmlDocument doc, Uri url) { + private static Seria GetSeria(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("a[href^=/series/]"); - if (a == default) { - return default; + if (a == null) + { + return null; } var span = a.QuerySelector("+ span"); - if (span == default) { - return default; + if (span == null) + { + return null; } - return new Seria { - Name = a.GetText()[5..].Trim(), - Number = span.GetText().Trim().Trim('#'), - Url = url.MakeRelativeUri(a.Attributes["href"].Value) + return new Seria + { + Name = a.GetText()[5..].Trim(), Number = span.GetText().Trim().Trim('#'), Url = url.MakeRelativeUri(a.Attributes["href"].Value) }; } - private async Task> FillChapters(Uri uri, string bookId) { + private async Task> FillChapters(Uri uri, string bookId) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - + var internalId = await GetInternalBookId(bookId); - - foreach (var bookChapter in await GetToc(internalId)) { + + foreach (var bookChapter in await GetToc(internalId)) + { Config.Logger.LogInformation($"Загружаю главу {bookChapter.Name.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = bookChapter.Name }; - + var doc = await GetChapter(bookChapter.Id); - if (doc != default) { + if (doc != null) + { chapter.Images = await GetImages(doc, uri); chapter.Content = doc.DocumentNode.InnerHtml; } - + result.Add(chapter); } return result; } - - protected virtual HttpRequestMessage GetMessage(Uri uri) { + + protected virtual HttpRequestMessage GetMessage(Uri uri) + { var message = new HttpRequestMessage(HttpMethod.Get, uri); message.Version = Config.Client.DefaultRequestVersion; - if (!string.IsNullOrWhiteSpace(_token)) { + if (!string.IsNullOrWhiteSpace(_token)) + { message.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token); } - foreach (var header in Config.Client.DefaultRequestHeaders) { + foreach (var header in Config.Client.DefaultRequestHeaders) + { message.Headers.Add(header.Key, header.Value); } - + return message; } - private async Task GetInternalBookId(string bookId) { + private async Task GetInternalBookId(string bookId) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri($"/reader/{bookId}")); return GetNextData(doc, "book").Book.Id.ToString(); } - private static T GetNextData(HtmlDocument doc, string node) { + private static T GetNextData(HtmlDocument doc, string node) + { var json = doc.QuerySelector("#__NEXT_DATA__").InnerText; return JsonDocument.Parse(json) @@ -146,24 +163,28 @@ private static T GetNextData(HtmlDocument doc, string node) { .Deserialize(); } - private async Task GetChapter(long bookChapterId) { + private async Task GetChapter(long bookChapterId) + { var response = await Config.Client.SendAsync(GetMessage(_apiUrl.MakeRelativeUri($"/api/v1/books/chapter/text/{bookChapterId}"))); - if (response.StatusCode == HttpStatusCode.Forbidden) { - return default; + if (response.StatusCode == HttpStatusCode.Forbidden) + { + return null; } - + var content = await response.Content.ReadAsStringAsync(); return content.Deserialize>().Data.Content.AsHtmlDoc(); } - private async Task> GetToc(string bookId) { + private async Task> GetToc(string bookId) + { var response = await Config.Client.GetWithTriesAsync(_apiUrl.MakeRelativeUri($"/api/v1/books/chapters/text/published?bookId={bookId}")); var content = await response.Content.ReadAsStringAsync(); return SliceToc(content.Deserialize>().Data, c => c.Name); } - private Task GetCover(HtmlDocument doc, Uri uri) { + private Task GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("img[itemprop=contentUrl]")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/BookinistGetter.cs b/src/Core/Logic/Getters/BookinistGetter.cs similarity index 72% rename from Core/Logic/Getters/BookinistGetter.cs rename to src/Core/Logic/Getters/BookinistGetter.cs index 77592651..3fd50ebe 100644 --- a/Core/Logic/Getters/BookinistGetter.cs +++ b/src/Core/Logic/Getters/BookinistGetter.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -10,36 +6,41 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class BookinistGetter(BookGetterConfig config) : GetterBase(config) { +public class BookinistGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://bookinist.pw/"); - public override async Task Get(Uri url) { + + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri($"/book/{GetId(url)}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { - Cover = await GetCover(doc, url), - Chapters = await FillChapters(doc, url), - Title = doc.GetTextBySelector("title"), - Author = new Author("Bookinist") + var book = new Book(url) + { + Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("title"), Author = new Author("Bookinist") }; - + return book; } - private async Task> FillChapters(HtmlDocument doc, Uri url) { + private async Task> FillChapters(HtmlDocument doc, Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var bookChapter in GetToc(doc, url)) { + foreach (var bookChapter in GetToc(doc, url)) + { Config.Logger.LogInformation($"Загружаю главу {bookChapter.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = bookChapter.Title }; - + var chapterDoc = await GetChapter(bookChapter); chapter.Images = await GetImages(chapterDoc, url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; @@ -49,19 +50,22 @@ private async Task> FillChapters(HtmlDocument doc, Uri url) return result; } - - private Task GetCover(HtmlDocument doc, Uri uri) { + + private Task GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("meta[property='og:image']")?.Attributes["content"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - private Task GetChapter(UrlChapter bookChapter) { + private Task GetChapter(UrlChapter bookChapter) + { return Config.Client.GetHtmlDocWithTriesAsync(bookChapter.Url).ContinueWith(t => t.Result.RemoveNodes("h2")); } - private IEnumerable GetToc(HtmlDocument doc, Uri url) { + private IEnumerable GetToc(HtmlDocument doc, Uri url) + { var bookId = GetId(url); var result = doc.QuerySelectorAll("ul.menu-toc li a").Skip(1).Select((a, i) => new UrlChapter(SystemUrl.MakeRelativeUri($"/bookpage/{bookId}/{i + 1}"), a.GetText())).ToList(); return SliceToc(result, c => c.Title); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/BooksYandex/BooksYandexBooksGetter.cs b/src/Core/Logic/Getters/BooksYandex/BooksYandexBooksGetter.cs similarity index 66% rename from Core/Logic/Getters/BooksYandex/BooksYandexBooksGetter.cs rename to src/Core/Logic/Getters/BooksYandex/BooksYandexBooksGetter.cs index 52f55fb5..780931cc 100644 --- a/Core/Logic/Getters/BooksYandex/BooksYandexBooksGetter.cs +++ b/src/Core/Logic/Getters/BooksYandex/BooksYandexBooksGetter.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Net.Http; using System.Net.Http.Json; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Misc; @@ -15,29 +10,40 @@ namespace Core.Logic.Getters.BooksYandex; -public class BooksYandexBooksGetter(BookGetterConfig config) : BooksYandexGetterBase(config) { +public class BooksYandexBooksGetter(BookGetterConfig config) : BooksYandexGetterBase(config) +{ protected override string[] Paths => ["books", "audiobooks", "serials"]; - protected override async Task> FillChapters(Book book, BooksYandexResponse response) { - if (Config.Options.HasAdditionalType(AdditionalTypeEnum.Audio)) { + protected override async Task> FillChapters(Book book, BooksYandexResponse response) + { + if (Config.Options.HasAdditionalType(AdditionalTypeEnum.Audio)) + { book.AdditionalFiles.Add(AdditionalTypeEnum.Audio, await GetAudio(response)); } var id = response.Book?.UUID ?? response.AudioBook?.LinkedBooks?.FirstOrDefault(); - if (string.IsNullOrWhiteSpace(id)) { + if (string.IsNullOrWhiteSpace(id)) + { return []; } - - if (response.Book?.SourceType == "serial") { + + if (response.Book?.SourceType == "serial") + { return await GetFromSerial(book, id); } - + var requestUri = $"https://api.bookmate.ru/api/v5/books/{id}/content/v4".AsUri(); var epubResponse = await Config.Client.GetAsync(requestUri); - if (epubResponse.StatusCode == HttpStatusCode.OK) { - var epubFile = await TempFile.Create(requestUri, Config.TempFolder.Path, epubResponse.Content.Headers.ContentDisposition.FileName.Trim('\"'), await epubResponse.Content.ReadAsStreamAsync()); - - if (epubFile != default && Config.Options.HasAdditionalType(AdditionalTypeEnum.Books)) { + if (epubResponse.StatusCode == HttpStatusCode.OK) + { + var epubFile = await TempFile.Create( + requestUri, + Config.TempFolder.Path, + epubResponse.Content.Headers.ContentDisposition.FileName.Trim('\"'), + await epubResponse.Content.ReadAsStreamAsync()); + + if (epubFile != null && Config.Options.HasAdditionalType(AdditionalTypeEnum.Books)) + { book.AdditionalFiles.Add(AdditionalTypeEnum.Books, epubFile); } @@ -47,77 +53,95 @@ protected override async Task> FillChapters(Book book, Book return []; } - private async Task> GetFromSerial(Book book, string id) { + private async Task> GetFromSerial(Book book, string id) + { var episodes = await GetEpisodes(id); var result = new List(); - - foreach (var episode in episodes.Where(e => e.CanBeRead)) { + + foreach (var episode in episodes.Where(e => e.CanBeRead)) + { Config.Logger.LogInformation($"Загружаю эпизод {episode.Title.CoverQuotes()}"); - + var requestUri = $"https://api.bookmate.ru/api/v5/books/{episode.Uuid}/content/v4".AsUri(); var epubResponse = await Config.Client.GetAsync(requestUri); - if (epubResponse.StatusCode != HttpStatusCode.OK) { + if (epubResponse.StatusCode != HttpStatusCode.OK) + { continue; } - var epubFile = await TempFile.Create(requestUri, Config.TempFolder.Path, epubResponse.Content.Headers.ContentDisposition.FileName.Trim('\"'), await epubResponse.Content.ReadAsStreamAsync()); - - if (epubFile != default && Config.Options.HasAdditionalType(AdditionalTypeEnum.Books)) { + var epubFile = await TempFile.Create( + requestUri, + Config.TempFolder.Path, + epubResponse.Content.Headers.ContentDisposition.FileName.Trim('\"'), + await epubResponse.Content.ReadAsStreamAsync()); + + if (epubFile != null && Config.Options.HasAdditionalType(AdditionalTypeEnum.Books)) + { book.AdditionalFiles.Add(AdditionalTypeEnum.Books, epubFile); } var episodeChapters = await FillChaptersFromEpub(epubFile); - - var chapter = new Chapter { - Title = episode.Title, - Content = string.Join(Environment.NewLine, episodeChapters.Select(c => c.Content)), - Images = episodeChapters.SelectMany(c => c.Images) + + var chapter = new Chapter + { + Title = episode.Title, Content = string.Join(Environment.NewLine, episodeChapters.Select(c => c.Content)), Images = episodeChapters.SelectMany(c => c.Images) }; - + result.Add(chapter); } return result; } - - private async Task> GetEpisodes(string id) { - try { + + private async Task> GetEpisodes(string id) + { + try + { var result = new List(); - for (var i = 1;; i++) { + for (var i = 1;; i++) + { var response = await Config.Client.GetFromJsonAsync($"https://api.bookmate.ru/api/v5/books/{id}/episodes".AsUri().AppendQueryParameter("page", i)); - if (response.Episodes.Length == 0) { + if (response.Episodes.Length == 0) + { break; } - + result.AddRange(response.Episodes); } - + return result; - } catch (HttpRequestException ex) { - if (ex.StatusCode == HttpStatusCode.Unauthorized) { + } + catch (HttpRequestException ex) + { + if (ex.StatusCode == HttpStatusCode.Unauthorized) + { throw new Exception("Авторизационный токен невалиден. Требуется обновление"); } throw; } } - - private async Task> GetAudio(BooksYandexResponse bookResponse) { + + private async Task> GetAudio(BooksYandexResponse bookResponse) + { var result = new List(); var id = bookResponse.AudioBook?.UUID ?? bookResponse.Book?.LinkedAudio?.FirstOrDefault(); - if (string.IsNullOrWhiteSpace(id)) { + if (string.IsNullOrWhiteSpace(id)) + { return result; } - + var playlist = await GetPlayList(id); - - if (playlist.Tracks.Length == 0) { + + if (playlist.Tracks.Length == 0) + { return result; } - for (var i = 0; i < playlist.Tracks.Length; i++) { + for (var i = 0; i < playlist.Tracks.Length; i++) + { var track = playlist.Tracks[i]; var url = track.Offline.Max.Url.Replace(".m3u8", ".m4a"); @@ -129,16 +153,21 @@ private async Task> GetAudio(BooksYandexResponse bookResponse) { return result; } - - private async Task GetPlayList(string id) { - try { + + private async Task GetPlayList(string id) + { + try + { return await Config.Client.GetFromJsonAsync($"https://api.bookmate.ru/api/v5/audiobooks/{id}/playlists.json".AsUri()); - } catch (HttpRequestException ex) { - if (ex.StatusCode == HttpStatusCode.Unauthorized) { + } + catch (HttpRequestException ex) + { + if (ex.StatusCode == HttpStatusCode.Unauthorized) + { throw new Exception("Авторизационный токен невалиден. Требуется обновление"); } throw; } } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/BooksYandex/BooksYandexComicsGetter.cs b/src/Core/Logic/Getters/BooksYandex/BooksYandexComicsGetter.cs similarity index 65% rename from Core/Logic/Getters/BooksYandex/BooksYandexComicsGetter.cs rename to src/Core/Logic/Getters/BooksYandex/BooksYandexComicsGetter.cs index 50a1b151..c521e033 100644 --- a/Core/Logic/Getters/BooksYandex/BooksYandexComicsGetter.cs +++ b/src/Core/Logic/Getters/BooksYandex/BooksYandexComicsGetter.cs @@ -1,7 +1,5 @@ -using System.Collections.Generic; using System.Net.Http.Json; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -9,29 +7,32 @@ namespace Core.Logic.Getters.BooksYandex; -public class BooksYandexComicsGetter(BookGetterConfig config) : BooksYandexGetterBase(config) { +public class BooksYandexComicsGetter(BookGetterConfig config) : BooksYandexGetterBase(config) +{ protected override string[] Paths => ["comicbooks"]; - protected override async Task> FillChapters(Book book, BooksYandexResponse response) { - if (Config.Options.NoChapters || string.IsNullOrWhiteSpace(response.Comicbook?.UUID)) { + protected override async Task> FillChapters(Book book, BooksYandexResponse response) + { + if (Config.Options.NoChapters || string.IsNullOrWhiteSpace(response.Comicbook?.UUID)) + { return []; } - + var metadata = await Config.Client.GetFromJsonAsync($"https://api.bookmate.ru/api/v5/comicbooks/{response.Comicbook.UUID}/metadata.json"); - + var sb = new StringBuilder(); - - foreach (var page in metadata.Pages) { + + foreach (var page in metadata.Pages) + { sb.Append($""); } var doc = sb.AsHtmlDoc(); - var chapter = new Chapter { - Images = await GetImages(doc, SystemUrl), - Content = doc.DocumentNode.InnerHtml, - Title = response.Comicbook.Title + var chapter = new Chapter + { + Images = await GetImages(doc, SystemUrl), Content = doc.DocumentNode.InnerHtml, Title = response.Comicbook.Title }; return [chapter]; } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/BooksYandex/BooksYandexGetterBase.cs b/src/Core/Logic/Getters/BooksYandex/BooksYandexGetterBase.cs similarity index 74% rename from Core/Logic/Getters/BooksYandex/BooksYandexGetterBase.cs rename to src/Core/Logic/Getters/BooksYandex/BooksYandexGetterBase.cs index e741ac3d..9578ea3c 100644 --- a/Core/Logic/Getters/BooksYandex/BooksYandexGetterBase.cs +++ b/src/Core/Logic/Getters/BooksYandex/BooksYandexGetterBase.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Net.Http; using System.Net.Http.Json; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -13,39 +8,46 @@ namespace Core.Logic.Getters.BooksYandex; -public abstract class BooksYandexGetterBase(BookGetterConfig config) : GetterBase(config) { +public abstract class BooksYandexGetterBase(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://books.yandex.ru/"); - + protected abstract string[] Paths { get; } - - protected override string GetId(Uri url) { + + protected override string GetId(Uri url) + { return url.GetSegment(2); } - public override bool IsSameUrl(Uri url) { + public override bool IsSameUrl(Uri url) + { return base.IsSameUrl(url) && Paths.Any(path => string.Equals(url.GetSegment(1), path, StringComparison.InvariantCultureIgnoreCase)); } - public override Task Authorize() { + public override Task Authorize() + { var token = Config.Options.Login ?? Config.Options.Password; - if (string.IsNullOrWhiteSpace(token)) { + if (string.IsNullOrWhiteSpace(token)) + { throw new Exception("Не указан авторизационный token."); } - + Config.Client.DefaultRequestHeaders.Add("auth-token", token); return Task.CompletedTask; } - - public override async Task Get(Uri url) { + + public override async Task Get(Uri url) + { var id = GetId(url); var path = url.GetSegment(1); url = SystemUrl.MakeRelativeUri($"/{path}/{id}"); - + var bookResponse = await GetBookResponse(path, id); var details = (BookmateBookBase)bookResponse.Book ?? (BookmateBookBase)bookResponse.AudioBook ?? bookResponse.Comicbook; - - var book = new Book(url) { + + var book = new Book(url) + { Cover = await GetCover(details), Title = details.Title, Author = GetAuthor(details), @@ -54,47 +56,58 @@ public override async Task Get(Uri url) { }; book.Chapters = await FillChapters(book, bookResponse); - + return book; } - protected virtual Task> FillChapters(Book book, BooksYandexResponse response) { + protected virtual Task> FillChapters(Book book, BooksYandexResponse response) + { return Task.FromResult>([]); } - - private async Task GetBookResponse(string path, string id) { - try { + + private async Task GetBookResponse(string path, string id) + { + try + { var response = await Config.Client.GetAsync($"https://api.bookmate.ru/api/v5/{path}/{id}".AsUri()); - if (response.StatusCode != HttpStatusCode.OK) { + if (response.StatusCode != HttpStatusCode.OK) + { response = await Config.Client.GetAsync($"https://api.bookmate.ru/api/v5/books/{id}".AsUri()); } var booksYandexResponse = await response.Content.ReadFromJsonAsync(); - - if (booksYandexResponse.AudioBook?.LinkedBooks?.Length > 0) { + + if (booksYandexResponse.AudioBook?.LinkedBooks?.Length > 0) + { var linkedResponse = await Config.Client.GetAsync($"https://api.bookmate.ru/api/v5/books/{booksYandexResponse.AudioBook.LinkedBooks[0]}".AsUri()); - if (linkedResponse.StatusCode == HttpStatusCode.OK) { + if (linkedResponse.StatusCode == HttpStatusCode.OK) + { return await linkedResponse.Content.ReadFromJsonAsync(); } } return booksYandexResponse; - } catch (HttpRequestException ex) { - if (ex.StatusCode == HttpStatusCode.Unauthorized) { + } + catch (HttpRequestException ex) + { + if (ex.StatusCode == HttpStatusCode.Unauthorized) + { throw new Exception("Авторизационный токен невалиден. Требуется обновление"); } throw; } } - - private Task GetCover(BookmateBookBase book) { + + private Task GetCover(BookmateBookBase book) + { var url = book.Cover.Large ?? book.Cover.Small; return !string.IsNullOrWhiteSpace(url) ? SaveImage(SystemUrl.MakeRelativeUri(url)) : Task.FromResult(default(TempFile)); } - - private Author GetAuthor(BookmateBookBase book) { + + private Author GetAuthor(BookmateBookBase book) + { var author = book.GetAuthor(); - return author == default ? new Author("BooksYandex") : new Author(author.Name, SystemUrl.MakeRelativeUri($"/authors/{author.Uuid}")); + return author == null ? new Author("BooksYandex") : new Author(author.Name, SystemUrl.MakeRelativeUri($"/authors/{author.Uuid}")); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/BookstabGetter.cs b/src/Core/Logic/Getters/BookstabGetter.cs similarity index 77% rename from Core/Logic/Getters/BookstabGetter.cs rename to src/Core/Logic/Getters/BookstabGetter.cs index 796b1611..b775c560 100644 --- a/Core/Logic/Getters/BookstabGetter.cs +++ b/src/Core/Logic/Getters/BookstabGetter.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; using System.Net.Http.Json; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -12,84 +8,99 @@ using HtmlAgilityPack; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class BookstabGetter(BookGetterConfig config) : GetterBase(config) { +public class BookstabGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://bookstab.ru/"); private Uri _apiUrl => new($"https://api.{SystemUrl.Host}/"); - protected override string GetId(Uri url) => url.GetSegment(2); + protected override string GetId(Uri url) + => url.GetSegment(2); - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var bookId = GetId(url); url = SystemUrl.MakeRelativeUri($"/book/{bookId}"); using var response = await Config.Client.GetWithTriesAsync(_apiUrl.MakeRelativeUri($"/api/reader-get/{bookId}")); var data = await response.Content.ReadFromJsonAsync(); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(data), Chapters = await FillChapters(data, url, bookId), Title = data?.Book.Title, Author = GetAuthor(data), Annotation = GetAnnotation(data?.Book) }; - + return book; } - private Author GetAuthor(BookstabApiResponse book) { + private Author GetAuthor(BookstabApiResponse book) + { return new Author(book.Book.User.Pseudonym, SystemUrl.MakeRelativeUri($"/user/{book.Book.User.Name}")); } - - private static string GetAnnotation(BookstabBook book) { - return string.IsNullOrWhiteSpace(book.Excerpt) ? - string.Empty : + + private static string GetAnnotation(BookstabBook book) + { + return string.IsNullOrWhiteSpace(book.Excerpt) ? + string.Empty : string.Join("", book.Excerpt.Split("\n", StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim().CoverTag("p"))); } - private async Task> FillChapters(BookstabApiResponse response, Uri uri, string bookId) { + private async Task> FillChapters(BookstabApiResponse response, Uri uri, string bookId) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var bookChapter in SliceToc(response.Book.ChaptersShow, c => c.Title)) { - var chapter = new Chapter { + foreach (var bookChapter in SliceToc(response.Book.ChaptersShow, c => c.Title)) + { + var chapter = new Chapter + { Title = bookChapter.Title }; Config.Logger.LogInformation($"Загружаю главу {bookChapter.Title.CoverQuotes()}"); - + var doc = await GetChapter(bookChapter.Id, bookId); - if (doc != default) { + if (doc != null) + { chapter.Images = await GetImages(doc, uri); chapter.Content = doc.DocumentNode.InnerHtml; } - + result.Add(chapter); } return result; } - private async Task GetChapter(int chapterId, string bookId) { - while (true) { + private async Task GetChapter(int chapterId, string bookId) + { + while (true) + { using var response = await Config.Client.GetAsync(_apiUrl.MakeRelativeUri($"/api/reader-get/{bookId}/{chapterId}")); - if (response.StatusCode == HttpStatusCode.Forbidden) { + if (response.StatusCode == HttpStatusCode.Forbidden) + { Config.Logger.LogInformation("Очень много запросов. Подождем..."); await Task.Delay(TimeSpan.FromSeconds(10)); continue; } var data = await response.Content.ReadFromJsonAsync(); - return string.IsNullOrWhiteSpace(data?.Chapter.Body) ? default : data.Chapter.Body.AsHtmlDoc(); + return string.IsNullOrWhiteSpace(data?.Chapter.Body) ? null : data.Chapter.Body.AsHtmlDoc(); } } - private Task GetCover(BookstabApiResponse response) { + private Task GetCover(BookstabApiResponse response) + { var imagePath = response.Book.Image; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(_apiUrl.MakeRelativeUri($"/storage/{imagePath}")) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/BookstimeGetter.cs b/src/Core/Logic/Getters/BookstimeGetter.cs similarity index 78% rename from Core/Logic/Getters/BookstimeGetter.cs rename to src/Core/Logic/Getters/BookstimeGetter.cs index ff592d51..2e9f8054 100644 --- a/Core/Logic/Getters/BookstimeGetter.cs +++ b/src/Core/Logic/Getters/BookstimeGetter.cs @@ -1,11 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Net.Http; using System.Net.Http.Json; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -15,93 +10,117 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class BookstimeGetter(BookGetterConfig config) : GetterBase(config) { +public class BookstimeGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://bookstime.ru/"); - protected override string GetId(Uri url) { + protected override string GetId(Uri url) + { return url.GetSegment(2); } - public override async Task Init() { + public override async Task Init() + { await base.Init(); Config.Client.DefaultRequestHeaders.Add("X-OCTOBER-REQUEST-FLASH", "1"); Config.Client.DefaultRequestHeaders.Add("X-OCTOBER-REQUEST-HANDLER", "bookAccount::onSignin"); Config.Client.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest"); } - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } - + using var post = await Config.Client.PostAsync(SystemUrl, GenerateAuthData()); - if (post.StatusCode != HttpStatusCode.OK) { + if (post.StatusCode != HttpStatusCode.OK) + { var content = await post.Content.ReadAsStringAsync(); throw new Exception($"Не удалось авторизоваться. {content}"); } - + var response = await post.Content.ReadFromJsonAsync(); - if (string.IsNullOrWhiteSpace(response.XOctoberErrorMessage)) { + if (string.IsNullOrWhiteSpace(response.XOctoberErrorMessage)) + { Config.Logger.LogInformation("Успешно авторизовались"); - } else { + } + else + { throw new Exception($"Не удалось авторизоваться. {response.XOctoberErrorMessage}"); } } - private FormUrlEncodedContent GenerateAuthData() { - var payload = new Dictionary { - { "email", Config.Options.Login }, - { "password", Config.Options.Password }, + private FormUrlEncodedContent GenerateAuthData() + { + var payload = new Dictionary + { + { + "email", Config.Options.Login + }, + { + "password", Config.Options.Password + }, }; return new FormUrlEncodedContent(payload); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri("/book-card/" + GetId(url)); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("h1"), Author = GetAuthor(doc), Annotation = doc.QuerySelector("div[itemprop=description]")?.InnerHtml }; - + return book; } - private Author GetAuthor(HtmlDocument doc) { + private Author GetAuthor(HtmlDocument doc) + { var a = doc.QuerySelector("a.book-card-full__author"); return new Author(a.GetText(), SystemUrl.MakeRelativeUri(a.Attributes["href"].Value)); } - - private IEnumerable GetToc(HtmlDocument doc, Uri url) { + + private IEnumerable GetToc(HtmlDocument doc, Uri url) + { var result = doc .QuerySelectorAll("div.book-card-full-description__content a") .Select(a => new UrlChapter(url.MakeRelativeUri(a.Attributes["href"].Value), a.GetText().ReplaceNewLine())) .ToList(); - + return SliceToc(result, c => c.Title); } - private async Task> FillChapters(HtmlDocument doc, Uri url) { + private async Task> FillChapters(HtmlDocument doc, Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var urlChapter in GetToc(doc, url)) { + foreach (var urlChapter in GetToc(doc, url)) + { Config.Logger.LogInformation($"Загружаю главу {urlChapter.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = urlChapter.Title }; var chapterDoc = await GetChapter(urlChapter); - if (chapterDoc != default) { + if (chapterDoc != null) + { chapter.Images = await GetImages(chapterDoc, urlChapter.Url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } @@ -112,33 +131,37 @@ private async Task> FillChapters(HtmlDocument doc, Uri url) return result; } - private async Task GetChapter(UrlChapter urlChapter) { + private async Task GetChapter(UrlChapter urlChapter) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(urlChapter.Url); var reader = doc.QuerySelector("div.reader__body"); - if (reader == default) { - return default; + if (reader == null) + { + return null; } - + var text = new StringBuilder(); text.Append(reader.RemoveNodes("h2.ui-text-head--3-em, iframe").InnerHtml); - + var paging = doc .QuerySelectorAll("a.ui-pagination-item") .Where(a => int.TryParse(a.GetText(), out _)) .Select(a => int.Parse(a.GetText())) .ToList(); - + var pages = paging.Any() ? paging.Max() : 1; - for (var i = 2; i <= pages; i++) { + for (var i = 2; i <= pages; i++) + { doc = await Config.Client.GetHtmlDocWithTriesAsync(urlChapter.Url.AppendSegment(i.ToString())); text.Append(doc.QuerySelector("div.reader__body").RemoveNodes("h2.ui-text-head--3-em, iframe").InnerHtml); } - + return text.AsHtmlDoc(); } - private Task GetCover(HtmlDocument doc, Uri bookUri) { + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("div[itemprop=image] img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/BookurukGetter.cs b/src/Core/Logic/Getters/BookurukGetter.cs similarity index 73% rename from Core/Logic/Getters/BookurukGetter.cs rename to src/Core/Logic/Getters/BookurukGetter.cs index 58047b6b..6115f799 100644 --- a/Core/Logic/Getters/BookurukGetter.cs +++ b/src/Core/Logic/Getters/BookurukGetter.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -11,68 +6,88 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class BookurukGetter(BookGetterConfig config) : GetterBase(config) { +public class BookurukGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://bookuruk.com/"); - protected override string GetId(Uri url) { + protected override string GetId(Uri url) + { return url.GetSegment(2); } - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } var doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl); var token = doc.QuerySelector("#loginpopup input[name=_csrf-shop]").Attributes["value"].Value; - + doc = await Config.Client.PostHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri("/login"), GenerateAuthData(token)); var alert = doc.QuerySelector("#infoMess")?.GetText(); - if (string.IsNullOrWhiteSpace(alert)) { + if (string.IsNullOrWhiteSpace(alert)) + { Config.Logger.LogInformation("Успешно авторизовались"); - } else { + } + else + { throw new Exception($"Не удалось авторизоваться. {alert}"); } } - private FormUrlEncodedContent GenerateAuthData(string token) { - var payload = new Dictionary { - { "_csrf-shop", token }, - { "email", Config.Options.Login }, - { "password", Config.Options.Password }, + private FormUrlEncodedContent GenerateAuthData(string token) + { + var payload = new Dictionary + { + { + "_csrf-shop", token + }, + { + "email", Config.Options.Login + }, + { + "password", Config.Options.Password + }, }; return new FormUrlEncodedContent(payload); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = await GetMainUrl(url); url = SystemUrl.MakeRelativeUri($"/book/{GetId(url)}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - - var book = new Book(url) { + + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("h1"), Author = GetAuthor(doc), Annotation = doc.QuerySelector("div.single-prew__desc_anot")?.RemoveNodes(" > b")?.InnerHtml }; - + return book; } - private Author GetAuthor(HtmlDocument doc) { + private Author GetAuthor(HtmlDocument doc) + { var a = doc.QuerySelector("a.bookcard_author + a"); var href = a.Attributes["href"]?.Value; - return string.IsNullOrWhiteSpace(href) ? - new Author(a.GetText()) : + return string.IsNullOrWhiteSpace(href) ? + new Author(a.GetText()) : new Author(a.GetText(), SystemUrl.MakeRelativeUri(href)); } - private async Task GetMainUrl(Uri url) { - if (url.GetSegment(1) == "book") { + private async Task GetMainUrl(Uri url) + { + if (url.GetSegment(1) == "book") + { return url; } @@ -80,50 +95,58 @@ private async Task GetMainUrl(Uri url) { return url.MakeRelativeUri(doc.QuerySelector("div.content.aside-right li a").Attributes["href"].Value); } - private async Task> GetToc(HtmlDocument doc, Uri url) { + private async Task> GetToc(HtmlDocument doc, Uri url) + { var internalId = SystemUrl.MakeRelativeUri(doc.QuerySelectorAll("div.content.aside-right li a")[1].Attributes["href"].Value).GetSegment(2); doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri($"/book/chapters/{internalId}")); - + var result = doc .QuerySelectorAll("a.read-chapters-item") .Select(a => new UrlChapter(url.MakeRelativeUri(a.Attributes["href"].Value), a.GetText())) .ToList(); - + return SliceToc(result, c => c.Title); } - - private async Task> FillChapters(HtmlDocument doc, Uri uri) { + + private async Task> FillChapters(HtmlDocument doc, Uri uri) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var urlChapter in await GetToc(doc, uri)) { - var chapter = new Chapter { + foreach (var urlChapter in await GetToc(doc, uri)) + { + var chapter = new Chapter + { Title = urlChapter.Title }; Config.Logger.LogInformation($"Загружаю главу {urlChapter.Title.CoverQuotes()}"); var chapterDoc = await GetChapter(urlChapter.Url); - if (chapterDoc != default) { + if (chapterDoc != null) + { chapter.Images = await GetImages(chapterDoc, urlChapter.Url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } - + result.Add(chapter); } return result; } - private async Task GetChapter(Uri url) { + private async Task GetChapter(Uri url) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); return doc.QuerySelector("div.read-area").InnerHtml.AsHtmlDoc(); } - private Task GetCover(HtmlDocument doc, Uri uri) { + private Task GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("div.single-prew__img img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/BoostyGetter.cs b/src/Core/Logic/Getters/BoostyGetter.cs similarity index 73% rename from Core/Logic/Getters/BoostyGetter.cs rename to src/Core/Logic/Getters/BoostyGetter.cs index 57e3a479..ca89ddad 100644 --- a/Core/Logic/Getters/BoostyGetter.cs +++ b/src/Core/Logic/Getters/BoostyGetter.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; using System.Net.Http.Json; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -13,90 +10,109 @@ namespace Core.Logic.Getters; -public class BoostyGetter(BookGetterConfig config) : GetterBase(config) { +public class BoostyGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://boosty.to/"); private static Uri ApiUrl => new(" https://api.boosty.to/"); - protected override string GetId(Uri url) { + protected override string GetId(Uri url) + { return url.GetSegment(1); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var bookId = GetId(url); url = SystemUrl.MakeRelativeUri($"/{bookId}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(url, bookId), Title = "Exclusive content on Boosty", Author = GetAuthor(doc, url), Annotation = doc.QuerySelector("div.AboutAuthor_content_HprOc")?.InnerHtml, }; - + return book; } - private static Author GetAuthor(HtmlDocument doc, Uri url) { + private static Author GetAuthor(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("h1"); return new Author(a.GetText(), url); } - private async Task> FillChapters(Uri uri, string bookId) { + private async Task> FillChapters(Uri uri, string bookId) + { var result = new List(); - - foreach (var post in await GetToc(bookId)) { + + foreach (var post in await GetToc(bookId)) + { Config.Logger.LogInformation($"Загружаю главу {post.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = post.Title }; - + var doc = await GetChapter(post, bookId); - if (doc != default) { + if (doc != null) + { chapter.Images = await GetImages(doc, uri); chapter.Content = doc.DocumentNode.InnerHtml; } - + result.Add(chapter); } return result; } - private async Task GetChapter(BoostyPost post, string bookId) { - if (!post.HasAccess) { - return default; + private async Task GetChapter(BoostyPost post, string bookId) + { + if (!post.HasAccess) + { + return null; } - + var doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri($"/{bookId}/posts/{post.Id}")); doc = doc.QuerySelector("article.Post_content_ETkyT").InnerHtml.AsHtmlDoc().RemoveNodes("h1"); - - foreach (var img in doc.QuerySelectorAll("img")) { - if (img.ParentNode.Name == "a") { + + foreach (var img in doc.QuerySelectorAll("img")) + { + if (img.ParentNode.Name == "a") + { img.ParentNode.Name = "div"; img.ParentNode.Attributes.RemoveAll(); } } - foreach (var div in doc.QuerySelectorAll("div.BlockRenderer_markup_Wtipg")) { - if (string.IsNullOrWhiteSpace(div.InnerHtml)) { + foreach (var div in doc.QuerySelectorAll("div.BlockRenderer_markup_Wtipg")) + { + if (string.IsNullOrWhiteSpace(div.InnerHtml)) + { div.InnerHtml = "
"; } } - + return doc; } - private async Task> GetToc(string bookId) { + private async Task> GetToc(string bookId) + { var result = new List(); - + var response = await Config.Client.GetWithTriesAsync(ApiUrl.MakeRelativeUri($"/v1/blog/{bookId}/post/").AppendQueryParameter("limit", 10)); var content = await response.Content.ReadFromJsonAsync>(); result.AddRange(content.Data); - while (!content.Extra.IsLast) { - response = await Config.Client.GetWithTriesAsync(ApiUrl.MakeRelativeUri($"/v1/blog/{bookId}/post/").AppendQueryParameter("limit", 10).AppendQueryParameter("offset", content.Extra.Offset)); + while (!content.Extra.IsLast) + { + response = await Config.Client.GetWithTriesAsync( + ApiUrl.MakeRelativeUri($"/v1/blog/{bookId}/post/").AppendQueryParameter("limit", 10).AppendQueryParameter("offset", content.Extra.Offset)); + content = await response.Content.ReadFromJsonAsync>(); result.AddRange(content.Data); } @@ -105,8 +121,9 @@ private async Task> GetToc(string bookId) { return SliceToc(result, c => c.Title); } - private Task GetCover(HtmlDocument doc, Uri uri) { + private Task GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("link[rel=image_src]")?.Attributes["href"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/BoovelGetter.cs b/src/Core/Logic/Getters/BoovelGetter.cs similarity index 83% rename from Core/Logic/Getters/BoovelGetter.cs rename to src/Core/Logic/Getters/BoovelGetter.cs index 731a56f9..620cbb72 100644 --- a/Core/Logic/Getters/BoovelGetter.cs +++ b/src/Core/Logic/Getters/BoovelGetter.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Security.Cryptography; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -15,55 +10,64 @@ namespace Core.Logic.Getters; -public class BoovelGetter(BookGetterConfig config) : GetterBase(config) { - +public class BoovelGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://boovell.ru/"); - protected override string GetId(Uri url) { + protected override string GetId(Uri url) + { return url.GetSegment(2); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri($"/story/{GetId(url)}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(doc), Title = doc.GetTextBySelector("h1.story__identity-title"), Author = new Author("Boovell"), Annotation = doc.QuerySelector("section.story__summary")?.InnerHtml }; - + return book; } - private async Task> FillChapters(HtmlDocument doc) { + private async Task> FillChapters(HtmlDocument doc) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - - foreach (var urlChapter in GetToc(doc)) { + + foreach (var urlChapter in GetToc(doc)) + { Config.Logger.LogInformation($"Загружаю главу {urlChapter.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = urlChapter.Title }; var chapterDoc = await GetChapter(urlChapter); - if (chapterDoc != default) { + if (chapterDoc != null) + { chapter.Images = await GetImages(chapterDoc, SystemUrl); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } - + result.Add(chapter); } return result; } - private IEnumerable GetToc(HtmlDocument doc) { + private IEnumerable GetToc(HtmlDocument doc) + { var result = doc .QuerySelectorAll("a.chapter-group__list-item-link[href]") .Select(a => new UrlChapter(SystemUrl.MakeRelativeUri(a.Attributes["href"].Value), a.GetText().ReplaceNewLine())) @@ -72,31 +76,33 @@ private IEnumerable GetToc(HtmlDocument doc) { return SliceToc(result, c => c.Title); } - private async Task GetChapter(UrlChapter urlChapter) { + private async Task GetChapter(UrlChapter urlChapter) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(urlChapter.Url); var encoded = Convert.FromBase64String(doc.QuerySelector("#chapter-content").InnerText); var secret = doc.QuerySelector("body").Attributes["data-post-id"].Value + "skajldayzonePVPkruto0"; var crypto = SHA256.HashData(Encoding.UTF8.GetBytes(secret).AsSpan(0, Encoding.UTF8.GetByteCount(secret))); using var aes = Aes.Create(); - + const int IV_SHIFT = 16; - aes.Key = crypto; + aes.Key = crypto; aes.IV = encoded[..IV_SHIFT]; - + var decryptor = aes.CreateDecryptor(); using var ms = new MemoryStream(encoded[IV_SHIFT..]); await using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read); - + var output = new MemoryStream(); await cs.CopyToAsync(output); return Encoding.UTF8.GetString(output.ToArray()).AsHtmlDoc(); } - private Task GetCover(HtmlDocument doc, Uri bookUri) { + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("img.wp-post-image")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/DarkNovelsGetter.cs b/src/Core/Logic/Getters/DarkNovelsGetter.cs similarity index 73% rename from Core/Logic/Getters/DarkNovelsGetter.cs rename to src/Core/Logic/Getters/DarkNovelsGetter.cs index 4fa21d9d..f7a1351e 100644 --- a/Core/Logic/Getters/DarkNovelsGetter.cs +++ b/src/Core/Logic/Getters/DarkNovelsGetter.cs @@ -1,14 +1,8 @@ -using System; -using System.Collections.Generic; -using System.IO; using System.IO.Compression; -using System.Linq; using System.Net; -using System.Net.Http; using System.Net.Http.Json; using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -20,11 +14,13 @@ namespace Core.Logic.Getters; -public class DarkNovelsGetter : GetterBase { +public class DarkNovelsGetter : GetterBase +{ private static readonly Dictionary _map = new(); private const string ALPHABET = "аАбБвВгГдДеЕёЁжЖзЗиИйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩъЪыЫьЬэЭюЮяЯ"; - public DarkNovelsGetter(BookGetterConfig config) : base(config) { + public DarkNovelsGetter(BookGetterConfig config) : base(config) + { InitMap(); } @@ -32,37 +28,46 @@ public DarkNovelsGetter(BookGetterConfig config) : base(config) { private Uri _apiUrl => new($"https://api.{SystemUrl.Host}/"); - public override Task Init() { + public override Task Init() + { Config.Client.DefaultRequestVersion = HttpVersion.Version20; Config.Client.DefaultRequestHeaders.Add("User-Agent", "novels 1.0.3"); return Task.CompletedTask; } - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } - var payload = new { - email = Config.Options.Login, - password = Config.Options.Password + var payload = new + { + email = Config.Options.Login, password = Config.Options.Password }; var response = await Config.Client.PostAsJsonAsync(_apiUrl.MakeRelativeUri("/v1/users/login"), payload); var data = await response.Content.ReadFromJsonAsync>(); - if (data!.Status == "success") { + if (data!.Status == "success") + { Config.Client.DefaultRequestHeaders.Add("Token", data.Data.Token.AccessToken); Config.Logger.LogInformation("Успешно авторизовались"); - } else { + } + else + { throw new Exception($"Не удалось авторизоваться. {data.Message}"); } } - private static void InitMap() { + private static void InitMap() + { var start = 13338; const int shift = 38; - foreach (var c in ALPHABET) { - for (var i = start; i < start + shift; i++) { + foreach (var c in ALPHABET) + { + for (var i = start; i < start + shift; i++) + { _map[i] = c; } @@ -70,7 +75,8 @@ private static void InitMap() { } } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = await GetMainUrl(url); var bookFullId = GetId(url); @@ -79,7 +85,8 @@ public override async Task Get(Uri url) { var uri = SystemUrl.MakeRelativeUri($"/{bookFullId}/"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(uri); - var book = new Book(uri) { + var book = new Book(uri) + { Cover = await GetCover(doc, uri), Chapters = await FillChapters(bookId), Title = doc.GetTextBySelector("h2.display-1"), @@ -90,13 +97,16 @@ public override async Task Get(Uri url) { return book; } - private static Author GetAuthor(HtmlDocument doc) { + private static Author GetAuthor(HtmlDocument doc) + { var match = Regex.Match(doc.ParsedText, "authors:\"(?.*?)\""); return new Author(match.Success ? match.Groups["author"].Value : "DarkNovels"); } - private async Task GetMainUrl(Uri url) { - if (url.GetSegment(1) == "read") { + private async Task GetMainUrl(Uri url) + { + if (url.GetSegment(1) == "read") + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var id = Regex.Match(doc.DocumentNode.InnerHtml, "slug:\"(?.*?)\""); return SystemUrl.MakeRelativeUri($"/{id.Groups["id"].Value}"); @@ -105,16 +115,20 @@ private async Task GetMainUrl(Uri url) { return url; } - private async Task> FillChapters(string bookId) { + private async Task> FillChapters(string bookId) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var darkNovelsChapter in await GetToc(bookId)) { + foreach (var darkNovelsChapter in await GetToc(bookId)) + { Config.Logger.LogInformation($"Загружаю главу {darkNovelsChapter.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = darkNovelsChapter.Title }; @@ -126,11 +140,14 @@ private async Task> FillChapters(string bookId) { return result; } - private Task GetCover(HtmlDocument doc, Uri bookUri) { + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("div.book-cover-container img")?.Attributes["data-src"]?.Value; - if (string.IsNullOrWhiteSpace(imagePath)) { + if (string.IsNullOrWhiteSpace(imagePath)) + { var match = new Regex("\"image\": \"(?.*?)\"").Match(doc.Text); - if (match.Success) { + if (match.Success) + { imagePath = match.Groups["url"].Value; } } @@ -138,38 +155,53 @@ private Task GetCover(HtmlDocument doc, Uri bookUri) { return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - private async Task> GetToc(string bookId) { + private async Task> GetToc(string bookId) + { return await Config.Client.GetFromJsonAsync>(_apiUrl.MakeRelativeUri($"/v2/toc/{bookId}")) .ContinueWith(t => SliceToc(t.Result?.Data.Where(c => !c.Title.StartsWith("Volume:")).ToList(), c => c.Title)); } - private async Task FillChapter(string bookId, DarkNovelsChapter darkNovelsChapter, Chapter chapter) { + private async Task FillChapter(string bookId, DarkNovelsChapter darkNovelsChapter, Chapter chapter) + { var data = await Config.Client.PostWithTriesAsync(_apiUrl.MakeRelativeUri("/v2/chapter/"), GetData(bookId, darkNovelsChapter.Id, "html"), TimeSpan.FromMilliseconds(200)); - if (data.StatusCode == HttpStatusCode.BadRequest) { + if (data.StatusCode == HttpStatusCode.BadRequest) + { return; } - + using var zip = new ZipArchive(await data.Content.ReadAsStreamAsync()); var sb = new StringBuilder(); - foreach (var entry in zip.Entries) { + foreach (var entry in zip.Entries) + { using var sr = new StreamReader(entry.Open()); - foreach (var c in await sr.ReadToEndAsync()) { + foreach (var c in await sr.ReadToEndAsync()) + { sb.Append(_map.GetValueOrDefault(c, c)); } } - + var chapterDoc = sb.AsHtmlDoc().RemoveNodes("h1"); chapter.Images = await GetImages(chapterDoc, SystemUrl); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } - private static MultipartFormDataContent GetData(string bookId, int chapterId, string format) { - return new MultipartFormDataContent { - { new StringContent(bookId), "b" }, - { new StringContent(format), "f" }, - { new StringContent("l"), "t" }, - { new StringContent(chapterId.ToString()), "c" } + private static MultipartFormDataContent GetData(string bookId, int chapterId, string format) + { + return new MultipartFormDataContent + { + { + new StringContent(bookId), "b" + }, + { + new StringContent(format), "f" + }, + { + new StringContent("l"), "t" + }, + { + new StringContent(chapterId.ToString()), "c" + } }; } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/DesuGetter.cs b/src/Core/Logic/Getters/DesuGetter.cs similarity index 75% rename from Core/Logic/Getters/DesuGetter.cs rename to src/Core/Logic/Getters/DesuGetter.cs index c96604a6..eca41fca 100644 --- a/Core/Logic/Getters/DesuGetter.cs +++ b/src/Core/Logic/Getters/DesuGetter.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -13,70 +9,83 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class DesuGetter(BookGetterConfig config) : GetterBase(config) { +public class DesuGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://desu.me/"); - protected override string GetId(Uri url) { + protected override string GetId(Uri url) + { return url.GetSegment(2); } - public override async Task Init() { + public override async Task Init() + { await base.Init(); Config.Client.DefaultRequestHeaders.Add("Referer", SystemUrl.ToString()); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri($"/manga/{GetId(url)}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(doc), Title = GetTitle(doc), Author = GetAuthor(doc), Annotation = doc.QuerySelector("#description div.prgrph")?.InnerHtml }; - + return book; } - private Author GetAuthor(HtmlDocument doc) { + private Author GetAuthor(HtmlDocument doc) + { var a = doc.QuerySelector("ul.translators a"); - return a == default ? new Author("Desu") : new Author(a.GetText(), SystemUrl.MakeRelativeUri(a.Attributes["href"].Value)); + return a == null ? new Author("Desu") : new Author(a.GetText(), SystemUrl.MakeRelativeUri(a.Attributes["href"].Value)); } - private static string GetTitle(HtmlDocument doc) { + private static string GetTitle(HtmlDocument doc) + { return doc.GetTextBySelector("h1 span.rus-name") ?? doc.GetTextBySelector("h1 span.name"); } - private async Task> FillChapters(HtmlDocument doc) { + private async Task> FillChapters(HtmlDocument doc) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - - foreach (var urlChapter in GetToc(doc)) { + + foreach (var urlChapter in GetToc(doc)) + { Config.Logger.LogInformation($"Загружаю главу {urlChapter.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = urlChapter.Title }; var chapterDoc = await GetChapter(urlChapter); - if (chapterDoc != default) { + if (chapterDoc != null) + { chapter.Images = await GetImages(chapterDoc, SystemUrl); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } - + result.Add(chapter); } return result; } - private IEnumerable GetToc(HtmlDocument doc) { + private IEnumerable GetToc(HtmlDocument doc) + { var result = doc .QuerySelectorAll("ul.chlist a[href]") .Select(a => new UrlChapter(SystemUrl.MakeRelativeUri(a.Attributes["href"].Value), a.GetText().ReplaceNewLine())) @@ -86,23 +95,28 @@ private IEnumerable GetToc(HtmlDocument doc) { return SliceToc(result, c => c.Title); } - private async Task GetChapter(UrlChapter urlChapter) { + private async Task GetChapter(UrlChapter urlChapter) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(urlChapter.Url.AppendQueryParameter("mtr", "1")); - var images = Regex.Match(doc.ParsedText, @"Reader\.init(.*?)(?\[\[(.*?)\]\]),", RegexOptions.Singleline).Groups["data"].Value.Replace("'", "\"").Deserialize>(); + var images = Regex.Match(doc.ParsedText, @"Reader\.init(.*?)(?\[\[(.*?)\]\]),", RegexOptions.Singleline).Groups["data"].Value.Replace("'", "\"") + .Deserialize>(); + var dir = Regex.Match(doc.ParsedText, "dir: \"(?(.*?))\"").Groups["data"].Value; var sb = new StringBuilder(); - - foreach (var elem in images) { + + foreach (var elem in images) + { var imageUri = SystemUrl.MakeRelativeUri(dir).MakeRelativeUri(elem[0].GetString()); sb.Append($""); } - + return sb.AsHtmlDoc(); } - private Task GetCover(HtmlDocument doc, Uri bookUri) { + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("div.c-poster img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/DreameGetter.cs b/src/Core/Logic/Getters/DreameGetter.cs similarity index 73% rename from Core/Logic/Getters/DreameGetter.cs rename to src/Core/Logic/Getters/DreameGetter.cs index 52881e95..469d2939 100644 --- a/Core/Logic/Getters/DreameGetter.cs +++ b/src/Core/Logic/Getters/DreameGetter.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; using System.Text; using System.Text.Json; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -12,22 +9,26 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class DreameGetter(BookGetterConfig config) : GetterBase(config) { +public class DreameGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://dreame.com/"); - + private Uri _apiUrl => new($"https://wap-api.{SystemUrl.Host}/"); - protected override string GetId(Uri url) => url.GetSegment(2).Split(".")[0].Split("-")[0]; + protected override string GetId(Uri url) + => url.GetSegment(2).Split(".")[0].Split("-")[0]; - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var bookId = GetId(url); url = $"https://www.dreame.com/story/{bookId}".AsUri(); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var data = GetNextData(doc, "novel"); - - var book = new Book(url) { + + var book = new Book(url) + { Cover = await GetCover(data.Cover), Chapters = await FillChapters(url, bookId), Title = data.Name, @@ -35,11 +36,12 @@ public override async Task Get(Uri url) { Annotation = data.Description, Lang = data.Language }; - + return book; } - - private static T GetNextData(HtmlDocument doc, string node) { + + private static T GetNextData(HtmlDocument doc, string node) + { var json = doc.QuerySelector("#__NEXT_DATA__").InnerText; return JsonDocument.Parse(json) .RootElement.GetProperty("props") @@ -49,49 +51,62 @@ private static T GetNextData(HtmlDocument doc, string node) { .GetRawText() .Deserialize(); } - - private async Task> FillChapters(Uri url, string bookId) { + + private async Task> FillChapters(Uri url, string bookId) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var urlChapter in await GetToc(bookId)) { + foreach (var urlChapter in await GetToc(bookId)) + { Config.Logger.LogInformation($"Загружаю главу {urlChapter.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = urlChapter.Title }; var chapterDoc = await GetChapter(urlChapter); - if (chapterDoc != default) { + if (chapterDoc != null) + { chapter.Images = await GetImages(chapterDoc, url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } - + result.Add(chapter); } return result; } - private async Task GetChapter(DreameChapter urlChapter) { - var response = await Config.Client.GetFromJsonWithTriesAsync>(_apiUrl.MakeRelativeUri($"/novel/getChapter?channel=dreamepmian-173&product=1&osType=2&cid={urlChapter.Id}&systemFlag=android&port=web")); + private async Task GetChapter(DreameChapter urlChapter) + { + var response = await Config.Client.GetFromJsonWithTriesAsync>( + _apiUrl.MakeRelativeUri($"/novel/getChapter?channel=dreamepmian-173&product=1&osType=2&cid={urlChapter.Id}&systemFlag=android&port=web")); + return Encoding.UTF8.GetString(Convert.FromBase64String(response.Data.Content)).AsHtmlDoc(); } - private async Task> GetToc(string bookId) { - var response = await Config.Client.GetFromJsonWithTriesAsync>(_apiUrl.MakeRelativeUri($"novel/getcatelog?channel=dreamepmian-173&product=1&osType=2&nid={bookId}")); + private async Task> GetToc(string bookId) + { + var response = await Config.Client.GetFromJsonWithTriesAsync>( + _apiUrl.MakeRelativeUri($"novel/getcatelog?channel=dreamepmian-173&product=1&osType=2&nid={bookId}")); + return SliceToc(response.Data.Pager.ChapterList, c => c.Title); } - private Author GetAuthor(HtmlDocument doc, DreameNovel data) { + private Author GetAuthor(HtmlDocument doc, DreameNovel data) + { var a = doc.QuerySelector("a a[class*=story_author-name]"); - return a != default ? - new Author(data.AuthorName, SystemUrl.MakeRelativeUri(a.Attributes["href"].Value)) : + return a != null ? + new Author(data.AuthorName, SystemUrl.MakeRelativeUri(a.Attributes["href"].Value)) : new Author(data.AuthorName); } - private Task GetCover(string url) { + private Task GetCover(string url) + { return !string.IsNullOrWhiteSpace(url) ? SaveImage(url.AsUri()) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/FanFicusGetter.cs b/src/Core/Logic/Getters/FanFicusGetter.cs similarity index 74% rename from Core/Logic/Getters/FanFicusGetter.cs rename to src/Core/Logic/Getters/FanFicusGetter.cs index 9fc58eff..c518327d 100644 --- a/Core/Logic/Getters/FanFicusGetter.cs +++ b/src/Core/Logic/Getters/FanFicusGetter.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; using System.Net.Http.Headers; using System.Net.Http.Json; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -15,96 +11,115 @@ namespace Core.Logic.Getters; -public class FanFicusGetter(BookGetterConfig config) : GetterBase(config) { +public class FanFicusGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://fanficus.com/"); - + private readonly Uri _apiHost = new("https://fanficus-server-mirror-879c30cd977f.herokuapp.com/"); - protected override string GetId(Uri url) { + protected override string GetId(Uri url) + { return url.GetSegment(2); } - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } - + var response = await Config.Client.PostAsJsonAsync(_apiHost.MakeRelativeUri("api/v1/user/login"), GenerateAuthData()); - if (response.StatusCode == HttpStatusCode.OK) { + if (response.StatusCode == HttpStatusCode.OK) + { var user = await response.Content.ReadFromJsonAsync>(); Config.Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", user.Value.Token); Config.Logger.LogInformation("Успешно авторизовались"); - } else { + } + else + { throw new Exception("Не удалось авторизоваться. Неверный пароль или имейл"); } } - private object GenerateAuthData() { - return new { - username = Config.Options.Login, - password = Config.Options.Password, + private object GenerateAuthData() + { + return new + { + username = Config.Options.Login, password = Config.Options.Password, }; } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var bookId = GetId(url); - + url = SystemUrl.MakeRelativeUri($"/post/{bookId}"); var response = await Config.Client.GetFromJsonWithTriesAsync>(_apiHost.MakeRelativeUri($"api/v1/post/{bookId}")); - - var book = new Book(url) { + + var book = new Book(url) + { Cover = await GetCover(response.Value, url), Chapters = await FillChapters(bookId), Title = response.Value.Title, Author = GetAuthor(response.Value), Annotation = response.Value.Description }; - + return book; } - private Author GetAuthor(FanFicusBook book) { + private Author GetAuthor(FanFicusBook book) + { var creator = book.Creators.FirstOrDefault(); - return creator == default ? new Author("FanFicus") : new Author(creator.NickName, SystemUrl.MakeRelativeUri($"/user/{creator.Id}")); + return creator == null ? new Author("FanFicus") : new Author(creator.NickName, SystemUrl.MakeRelativeUri($"/user/{creator.Id}")); } - private async Task> GetToc(string bookId) { + private async Task> GetToc(string bookId) + { var response = await Config.Client.GetFromJsonWithTriesAsync>(_apiHost.MakeRelativeUri($"/api/v1/post/{bookId}/post-part")); return SliceToc(response.Value, c => c.Title); } - - private async Task> FillChapters(string bookId) { + + private async Task> FillChapters(string bookId) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var fanFicusPart in await GetToc(bookId)) { - var chapter = new Chapter { + foreach (var fanFicusPart in await GetToc(bookId)) + { + var chapter = new Chapter + { Title = fanFicusPart.Title }; Config.Logger.LogInformation($"Загружаю главу {fanFicusPart.Title.CoverQuotes()}"); var chapterDoc = await GetChapter(bookId, fanFicusPart); - if (chapterDoc != default) { + if (chapterDoc != null) + { chapter.Images = await GetImages(chapterDoc, SystemUrl); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } - + result.Add(chapter); } return result; } - private async Task GetChapter(string bookId, FanFicusPart fanFicusPart) { + private async Task GetChapter(string bookId, FanFicusPart fanFicusPart) + { var response = await Config.Client.GetFromJsonWithTriesAsync>(_apiHost.MakeRelativeUri($"api/v1/post/{bookId}/post-part/{fanFicusPart.Id}")); return response.Value.Text.AsHtmlDoc(); } - private Task GetCover(FanFicusBook doc, Uri uri) { + private Task GetCover(FanFicusBook doc, Uri uri) + { var imagePath = doc.Images.FirstOrDefault()?.Url; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Fb2Top/Fb2TopGetter.cs b/src/Core/Logic/Getters/Fb2Top/Fb2TopGetter.cs similarity index 68% rename from Core/Logic/Getters/Fb2Top/Fb2TopGetter.cs rename to src/Core/Logic/Getters/Fb2Top/Fb2TopGetter.cs index 6c8e70be..029330da 100644 --- a/Core/Logic/Getters/Fb2Top/Fb2TopGetter.cs +++ b/src/Core/Logic/Getters/Fb2Top/Fb2TopGetter.cs @@ -1,8 +1,8 @@ -using System; using Core.Configs; -namespace Core.Logic.Getters.Fb2Top; +namespace Core.Logic.Getters.Fb2Top; -public class Fb2TopGetter(BookGetterConfig config) : Fb2TopGetterBase(config) { +public class Fb2TopGetter(BookGetterConfig config) : Fb2TopGetterBase(config) +{ protected override Uri SystemUrl => new("https://fb2.top/"); -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Fb2Top/Fb2TopGetterBase.cs b/src/Core/Logic/Getters/Fb2Top/Fb2TopGetterBase.cs similarity index 69% rename from Core/Logic/Getters/Fb2Top/Fb2TopGetterBase.cs rename to src/Core/Logic/Getters/Fb2Top/Fb2TopGetterBase.cs index 70a41104..4a567ec1 100644 --- a/Core/Logic/Getters/Fb2Top/Fb2TopGetterBase.cs +++ b/src/Core/Logic/Getters/Fb2Top/Fb2TopGetterBase.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -10,16 +6,20 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.Fb2Top; +namespace Core.Logic.Getters.Fb2Top; -public abstract class Fb2TopGetterBase(BookGetterConfig config) : GetterBase(config) { - protected override string GetId(Uri url) => url.GetSegment(1); +public abstract class Fb2TopGetterBase(BookGetterConfig config) : GetterBase(config) +{ + protected override string GetId(Uri url) + => url.GetSegment(1); - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = url.MakeRelativeUri(GetId(url)); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - - var book = new Book(url) { + + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("h1.book-title"), @@ -27,67 +27,78 @@ public override async Task Get(Uri url) { Annotation = doc.QuerySelector("div.annotation")?.InnerHtml, Seria = GetSeria(doc, url) }; - + return book; } - private async Task> FillChapters(HtmlDocument doc, Uri url) { + private async Task> FillChapters(HtmlDocument doc, Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var urlChapter in GetToc(doc, url)) { + foreach (var urlChapter in GetToc(doc, url)) + { Config.Logger.LogInformation($"Загружаю главу {urlChapter.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = urlChapter.Title }; var chapterDoc = await GetChapter(urlChapter.Url); - if (chapterDoc != default) { + if (chapterDoc != null) + { chapter.Images = await GetImages(chapterDoc, urlChapter.Url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } - + result.Add(chapter); } return result; } - private async Task GetChapter(Uri url) { + private async Task GetChapter(Uri url) + { var doc = await Config.Client.GetStringAsync(url); doc = doc.Replace("", ""); doc = doc.Replace("

", "

"); return doc.AsHtmlDoc().QuerySelector("section").RemoveNodes("h3").InnerHtml.AsHtmlDoc(); } - private IEnumerable GetToc(HtmlDocument doc, Uri url) { + private IEnumerable GetToc(HtmlDocument doc, Uri url) + { var urlChapters = doc.QuerySelectorAll("div.card-body li a").Select(a => new UrlChapter(url.MakeRelativeUri(a.Attributes["href"].Value), a.GetText())).ToList(); return SliceToc(urlChapters, c => c.Title); } - private static Seria GetSeria(HtmlDocument doc, Uri url) { + private static Seria GetSeria(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("div.book-info-body a[href*=/series/]"); - if (a != default) { - return new Seria { - Name = a.GetText(), - Url = url.MakeRelativeUri(a.Attributes["href"].Value) + if (a != null) + { + return new Seria + { + Name = a.GetText(), Url = url.MakeRelativeUri(a.Attributes["href"].Value) }; } - return default; + return null; } - private static Author GetAuthor(HtmlDocument doc, Uri url) { + private static Author GetAuthor(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("div.book-info-body a[href*=/authors/]"); - return a != default ? - new Author(a.GetText().ReplaceNewLine(), url.MakeRelativeUri(a.Attributes["href"].Value)) : + return a != null ? + new Author(a.GetText().ReplaceNewLine(), url.MakeRelativeUri(a.Attributes["href"].Value)) : new Author("Fb2Top"); } - private Task GetCover(HtmlDocument doc, Uri url) { + private Task GetCover(HtmlDocument doc, Uri url) + { var imagePath = doc.QuerySelector("img.book-info-poster-img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(url.MakeRelativeUri(imagePath.HtmlDecode())) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Fb2Top/LadyLibGetter.cs b/src/Core/Logic/Getters/Fb2Top/LadyLibGetter.cs similarity index 68% rename from Core/Logic/Getters/Fb2Top/LadyLibGetter.cs rename to src/Core/Logic/Getters/Fb2Top/LadyLibGetter.cs index ec5f6453..6e2a776c 100644 --- a/Core/Logic/Getters/Fb2Top/LadyLibGetter.cs +++ b/src/Core/Logic/Getters/Fb2Top/LadyLibGetter.cs @@ -1,8 +1,8 @@ -using System; using Core.Configs; -namespace Core.Logic.Getters.Fb2Top; +namespace Core.Logic.Getters.Fb2Top; -public class LadyLibGetter(BookGetterConfig config) : Fb2TopGetterBase(config) { +public class LadyLibGetter(BookGetterConfig config) : Fb2TopGetterBase(config) +{ protected override Uri SystemUrl => new("https://ladylib.top/"); -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/FicbookGetter.cs b/src/Core/Logic/Getters/FicbookGetter.cs similarity index 74% rename from Core/Logic/Getters/FicbookGetter.cs rename to src/Core/Logic/Getters/FicbookGetter.cs index d85c8d6f..cdb6408d 100644 --- a/Core/Logic/Getters/FicbookGetter.cs +++ b/src/Core/Logic/Getters/FicbookGetter.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -11,57 +7,69 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class FicbookGetter(BookGetterConfig config) : GetterBase(config) { +public class FicbookGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://ficbook.net/"); - + protected Uri _appUrl => new("https://fanficlets.xyz/"); - protected override string GetId(Uri url) => url.GetSegment(2); + protected override string GetId(Uri url) + => url.GetSegment(2); - public override async Task Init() { + public override async Task Init() + { await base.Init(); Config.Client.DefaultRequestHeaders.Remove("User-Agent"); - Config.Client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 (iPhone); iOS CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1 ||| ficbook (1.3.2) | iOS (26.1) | iPhone 13 | ao2gvvnhkv0t22lo"); + Config.Client.DefaultRequestHeaders.TryAddWithoutValidation( + "User-Agent", + "Mozilla/5.0 (iPhone); iOS CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1 ||| ficbook (1.3.2) | iOS (26.1) | iPhone 13 | ao2gvvnhkv0t22lo"); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var appUrl = _appUrl.MakeRelativeUri($"/readfic/{GetId(url)}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(appUrl); var title = doc.GetTextBySelector("h1"); - var book = new Book(SystemUrl.MakeRelativeUri($"/readfic/{GetId(url)}")) { + var book = new Book(SystemUrl.MakeRelativeUri($"/readfic/{GetId(url)}")) + { Cover = await GetCover(doc, appUrl), Chapters = await FillChapters(doc, appUrl, title), Title = title, Author = GetAuthor(doc, appUrl), Annotation = doc.QuerySelector("div[itemprop=description]")?.InnerHtml }; - + return book; } - private static Author GetAuthor(HtmlDocument doc, Uri url) { + private static Author GetAuthor(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("a.creator-username"); return new Author(a.GetText(), url.MakeRelativeUri(a.Attributes["href"].Value)); } - - private Task GetCover(HtmlDocument doc, Uri bookUri) { + + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("fanfic-cover")?.Attributes["src-original"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - private async Task> FillChapters(HtmlDocument doc, Uri url, string title) { + private async Task> FillChapters(HtmlDocument doc, Uri url, string title) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - - foreach (var ficbookChapter in GetToc(doc, url, title)) { + + foreach (var ficbookChapter in GetToc(doc, url, title)) + { Config.Logger.LogInformation($"Загружаю главу {ficbookChapter.Title.CoverQuotes()}"); var chapter = new Chapter(); - + var chapterDoc = await GetChapter(ficbookChapter); chapter.Images = await GetImages(chapterDoc, url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; @@ -73,44 +81,54 @@ private async Task> FillChapters(HtmlDocument doc, Uri url, return result; } - private async Task GetChapter(UrlChapter urlChapter) { + private async Task GetChapter(UrlChapter urlChapter) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(urlChapter.Url); - + var content = doc.QuerySelector("#content").RemoveNodes("div"); using var sr = new StringReader(content.InnerText.HtmlDecode()); var text = new StringBuilder(); - while (true) { + while (true) + { var line = await sr.ReadLineAsync(); - if (line == null) { + if (line == null) + { break; } - if (string.IsNullOrWhiteSpace(line)) { + if (string.IsNullOrWhiteSpace(line)) + { continue; } - + text.Append(line.HtmlEncode().CoverTag("p")); } return text.AsHtmlDoc(); } - private IEnumerable GetToc(HtmlDocument doc, Uri url, string title) { + private IEnumerable GetToc(HtmlDocument doc, Uri url, string title) + { var result = new List(); - + var links = doc.QuerySelectorAll("li.part"); - if (links.Count == 0) { + if (links.Count == 0) + { result.Add(new UrlChapter(url, title)); - } else { - foreach (var li in links) { + } + else + { + foreach (var li in links) + { var a = li.QuerySelector("a.part-link"); - if (a != null) { + if (a != null) + { result.Add(new UrlChapter(url.MakeRelativeUri(a.Attributes["href"].Value), li.GetTextBySelector("h3"))); } } } - + return SliceToc(result, c => c.Title); } } diff --git a/Core/Logic/Getters/FictionBookGetter.cs b/src/Core/Logic/Getters/FictionBookGetter.cs similarity index 73% rename from Core/Logic/Getters/FictionBookGetter.cs rename to src/Core/Logic/Getters/FictionBookGetter.cs index a37bfdce..9c201be5 100644 --- a/Core/Logic/Getters/FictionBookGetter.cs +++ b/src/Core/Logic/Getters/FictionBookGetter.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -11,117 +7,143 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class FictionBookGetter(BookGetterConfig config) : GetterBase(config) { +public class FictionBookGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://fictionbook.ru"); - - private Uri GetMainUrl(Uri url) { - if (url.Segments.Last().StartsWith("read_online.html")) { + + private Uri GetMainUrl(Uri url) + { + if (url.Segments.Last().StartsWith("read_online.html")) + { url = SystemUrl.MakeRelativeUri(string.Join(string.Empty, url.Segments[..^1])); } return url; } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = GetMainUrl(url); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var name = doc.GetTextBySelector("h1 span[itemprop=name]"); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(url, name), Title = name, Author = GetAuthor(doc), Annotation = doc.QuerySelector("div.biblio_book__annotation")?.InnerHtml, }; - - return book; + + return book; } - private Author GetAuthor(HtmlDocument doc) { + private Author GetAuthor(HtmlDocument doc) + { var a = doc.QuerySelector("div.info a[data-widget-litres-author][href]"); - return a == default ? - new Author("FictionBook") : + return a == null ? + new Author("FictionBook") : new Author(a.GetText(), SystemUrl.MakeRelativeUri(a.Attributes["href"].Value)); } - private Task GetCover(HtmlDocument doc, Uri bookUri) { + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("div.cover_float img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - private async Task AddChapter(ICollection chapters, Chapter chapter, StringBuilder text) { - if (chapter == null) { + private async Task AddChapter(ICollection chapters, Chapter chapter, StringBuilder text) + { + if (chapter == null) + { return; } - + var chapterDoc = text.AsHtmlDoc(); chapter.Images = await GetImages(chapterDoc, SystemUrl.MakeRelativeUri("/br/")); chapter.Content = chapterDoc.DocumentNode.InnerHtml; chapters.Add(chapter); } - private static bool IsChapterHeader(HtmlNode node) { + private static bool IsChapterHeader(HtmlNode node) + { return node.Name == "h2"; } - private static bool IsSingleChapter(IEnumerable nodes) { + private static bool IsSingleChapter(IEnumerable nodes) + { var firstNode = nodes.First(); return !IsChapterHeader(firstNode) || string.IsNullOrWhiteSpace(firstNode.InnerText); } - private static Chapter CreateChapter(string title) { - return new Chapter { + private static Chapter CreateChapter(string title) + { + return new Chapter + { Title = title }; } - private Task GetChapter(Uri url, int page) { + private Task GetChapter(Uri url, int page) + { return Config.Client.GetHtmlDocWithTriesAsync(url.AppendSegment("read_online.html").AppendQueryParameter("page", page)); } - private async Task GetPages(Uri url) { + private async Task GetPages(Uri url) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(url.AppendSegment("read_online.html")); var pages = doc.QuerySelectorAll("div.reader_pager_bottom > a[href]") .Where(a => int.TryParse(a.GetText(), out _)) .Select(a => int.Parse(a.GetText())) .ToList(); - + return pages.Any() ? pages.Max() : 1; } - private async Task> FillChapters(Uri url, string name) { + private async Task> FillChapters(Uri url, string name) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - + Chapter chapter = null; var singleChapter = true; var text = new StringBuilder(); var pages = await GetPages(url); - - for (var i = 1; i <= pages; i++) { + + for (var i = 1; i <= pages; i++) + { Config.Logger.LogInformation($"Получаю страницу {i}/{pages}"); var page = await GetChapter(url, i); - var nodes = page.QuerySelector("#onlineread").RemoveNodes("div.biblio_book__wrap").ChildNodes; + var nodes = page.QuerySelector("#onlineread").RemoveNodes("div.biblio_book__wrap").ChildNodes; singleChapter = i == 1 ? IsSingleChapter(nodes) : singleChapter; - foreach (var node in nodes) { - if (singleChapter || !IsChapterHeader(node)) { - if (node.Name == "img" && node.Attributes["src"] != null) { + foreach (var node in nodes) + { + if (singleChapter || !IsChapterHeader(node)) + { + if (node.Name == "img" && node.Attributes["src"] != null) + { text.Append($""); - } else { - if (!string.IsNullOrWhiteSpace(node.InnerHtml)) { + } + else + { + if (!string.IsNullOrWhiteSpace(node.InnerHtml)) + { text.Append(node.InnerHtml.HtmlDecode().CoverTag("p")); } } - } else { + } + else + { await AddChapter(result, chapter, text); text.Clear(); chapter = CreateChapter(node.InnerText.HtmlDecode()); @@ -132,4 +154,4 @@ private async Task> FillChapters(Uri url, string name) { await AddChapter(result, chapter ?? CreateChapter(name), text); return result; } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/FreedlitGetter.cs b/src/Core/Logic/Getters/FreedlitGetter.cs similarity index 71% rename from Core/Logic/Getters/FreedlitGetter.cs rename to src/Core/Logic/Getters/FreedlitGetter.cs index 54fdf24f..047adf5f 100644 --- a/Core/Logic/Getters/FreedlitGetter.cs +++ b/src/Core/Logic/Getters/FreedlitGetter.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Json; using System.Text.Json.Nodes; -using System.Threading.Tasks; using System.Web; using Core.Configs; using Core.Extensions; @@ -14,13 +10,16 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class FreedlitGetter(BookGetterConfig config) : GetterBase(config) { +public class FreedlitGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://freedlit.space/"); - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } @@ -29,35 +28,41 @@ public override async Task Authorize() { using var post = await Config.Client.PostAsJsonAsync(SystemUrl.MakeRelativeUri("/login"), GenerateAuthData()); var doc = await post.Content.ReadAsStreamAsync().ContinueWith(c => c.Result.AsHtmlDoc()); var errors = doc.QuerySelector("#app").Attributes["data-page"].Value.HtmlDecode().Deserialize>().Props.Errors; - - if (string.IsNullOrWhiteSpace(errors?.Email)) { + + if (string.IsNullOrWhiteSpace(errors?.Email)) + { Config.Logger.LogInformation("Успешно авторизовались"); - } else { + } + else + { throw new Exception($"Не удалось авторизоваться. {errors.Email}"); } } - private object GenerateAuthData() { - return new { - email = Config.Options.Login, - password = Config.Options.Password, - remember = false, + private object GenerateAuthData() + { + return new + { + email = Config.Options.Login, password = Config.Options.Password, remember = false, }; } - private void SetToken() { + private void SetToken() + { Config.Client.DefaultRequestHeaders.Remove("X-XSRF-TOKEN"); Config.Client.DefaultRequestHeaders.Add("X-XSRF-TOKEN", HttpUtility.UrlDecode(Config.CookieContainer.GetAllCookies().FirstOrDefault(c => c.Name == "XSRF-TOKEN").Value)); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri($"/book/{GetId(url)}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var freedlitBook = doc.QuerySelector("#app").Attributes["data-page"].Value.HtmlDecode().Deserialize>().Props.Book; SetToken(); - - var book = new Book(url) { + + var book = new Book(url) + { Cover = await GetCover(freedlitBook), Chapters = await FillChapters(freedlitBook), Title = freedlitBook.Title, @@ -65,36 +70,49 @@ public override async Task Get(Uri url) { Annotation = freedlitBook.Annotation, Lang = freedlitBook.Language }; - + return book; } - private Author GetAuthor(FreedlitBook book) { + private Author GetAuthor(FreedlitBook book) + { return new Author(book.MainAuthor.Name, SystemUrl.MakeRelativeUri($"/p/{book.MainAuthor.UserLink}")); } - - private async Task> GetToc(FreedlitBook book) { - var response = await Config.Client.PostAsJsonAsync(SystemUrl.MakeRelativeUri("/api/bookpage/get-chapters"), new { book_id = book.Id }); + + private async Task> GetToc(FreedlitBook book) + { + var response = await Config.Client.PostAsJsonAsync( + SystemUrl.MakeRelativeUri("/api/bookpage/get-chapters"), + new + { + book_id = book.Id + }); + var data = await response.Content.ReadFromJsonAsync>>(); - + return SliceToc(data.Success.Items, c => c.Header); } - private async Task> FillChapters(FreedlitBook book) { + private async Task> FillChapters(FreedlitBook book) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var freedlitChapter in await GetToc(book)) { - var chapter = new Chapter { + foreach (var freedlitChapter in await GetToc(book)) + { + var chapter = new Chapter + { Title = freedlitChapter.Header }; Config.Logger.LogInformation($"Загружаю главу {freedlitChapter.Header.CoverQuotes()}"); var chapterDoc = await GetChapter(book, freedlitChapter); - if (chapterDoc != default) { + if (chapterDoc != null) + { chapter.Images = await GetImages(chapterDoc, SystemUrl); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } @@ -105,18 +123,27 @@ private async Task> FillChapters(FreedlitBook book) { return result; } - private async Task GetChapter(FreedlitBook book, FreedlitChapter chapter) { - var response = await Config.Client.PostAsJsonAsync(SystemUrl.MakeRelativeUri("/reader/get-content"), new { book_id = book.Id, chapter_id = chapter.Id }); + private async Task GetChapter(FreedlitBook book, FreedlitChapter chapter) + { + var response = await Config.Client.PostAsJsonAsync( + SystemUrl.MakeRelativeUri("/reader/get-content"), + new + { + book_id = book.Id, chapter_id = chapter.Id + }); + var fullChapter = await response.Content.ReadFromJsonAsync>(); - if (string.IsNullOrWhiteSpace(fullChapter.Success?.Content)) { - return default; + if (string.IsNullOrWhiteSpace(fullChapter.Success?.Content)) + { + return null; } - + var doc = fullChapter.Success.Content.AsHtmlDoc(); return doc.QuerySelector("body").InnerHtml.AsHtmlDoc(); } - private Task GetCover(FreedlitBook book) { + private Task GetCover(FreedlitBook book) + { return !string.IsNullOrWhiteSpace(book.Cover) ? SaveImage(SystemUrl.MakeRelativeUri($"/storage/{book.Cover}")) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Freedom/BookHamsterGetter.cs b/src/Core/Logic/Getters/Freedom/BookHamsterGetter.cs similarity index 84% rename from Core/Logic/Getters/Freedom/BookHamsterGetter.cs rename to src/Core/Logic/Getters/Freedom/BookHamsterGetter.cs index 7eae0c7a..7eaebc3b 100644 --- a/Core/Logic/Getters/Freedom/BookHamsterGetter.cs +++ b/src/Core/Logic/Getters/Freedom/BookHamsterGetter.cs @@ -1,8 +1,8 @@ -using System; using Core.Configs; namespace Core.Logic.Getters.Freedom; -public class BookHamsterGetter(BookGetterConfig config) : FreedomGetterBase(config) { +public class BookHamsterGetter(BookGetterConfig config) : FreedomGetterBase(config) +{ protected override Uri SystemUrl => new("https://bookhamster.ru/"); -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Freedom/FreedomGetter.cs b/src/Core/Logic/Getters/Freedom/FreedomGetter.cs similarity index 85% rename from Core/Logic/Getters/Freedom/FreedomGetter.cs rename to src/Core/Logic/Getters/Freedom/FreedomGetter.cs index 7cdd721e..5fd30127 100644 --- a/Core/Logic/Getters/Freedom/FreedomGetter.cs +++ b/src/Core/Logic/Getters/Freedom/FreedomGetter.cs @@ -1,8 +1,8 @@ -using System; using Core.Configs; namespace Core.Logic.Getters.Freedom; -public class FreedomGetter(BookGetterConfig config) : FreedomGetterBase(config) { +public class FreedomGetter(BookGetterConfig config) : FreedomGetterBase(config) +{ protected override Uri SystemUrl => new("https://ifreedom.su/"); -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Freedom/FreedomGetterBase.cs b/src/Core/Logic/Getters/Freedom/FreedomGetterBase.cs similarity index 69% rename from Core/Logic/Getters/Freedom/FreedomGetterBase.cs rename to src/Core/Logic/Getters/Freedom/FreedomGetterBase.cs index f01fe9b6..67a59bcc 100644 --- a/Core/Logic/Getters/Freedom/FreedomGetterBase.cs +++ b/src/Core/Logic/Getters/Freedom/FreedomGetterBase.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -10,26 +6,28 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.Freedom; +namespace Core.Logic.Getters.Freedom; -public abstract class FreedomGetterBase(BookGetterConfig config) : GetterBase(config) { - public override async Task Get(Uri url) { +public abstract class FreedomGetterBase(BookGetterConfig config) : GetterBase(config) +{ + public override async Task Get(Uri url) + { url = await GetMainUrl(url); url = SystemUrl.MakeRelativeUri($"/ranobe/{GetId(url)}/"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - - var book = new Book(url) { - Cover = await GetCover(doc, url), - Chapters = await FillChapters(doc, url), - Title = doc.GetTextBySelector("h1.entry-title"), - Author = GetAuthor() + + var book = new Book(url) + { + Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("h1.entry-title"), Author = GetAuthor() }; - + return book; } - private async Task GetMainUrl(Uri url) { - if (url.GetSegment(1) == "ranobe") { + private async Task GetMainUrl(Uri url) + { + if (url.GetSegment(1) == "ranobe") + { return url; } @@ -37,56 +35,65 @@ private async Task GetMainUrl(Uri url) { return url.MakeRelativeUri(doc.QuerySelector("div.bun2 a").Attributes["href"].Value); } - private IEnumerable GetToc(HtmlDocument doc, Uri url) { + private IEnumerable GetToc(HtmlDocument doc, Uri url) + { var result = doc .QuerySelectorAll("div.li-col1-ranobe > a") .Select(a => new UrlChapter(url.MakeRelativeUri(a.Attributes["href"].Value), a.GetText())) .Reverse() .ToList(); - + return SliceToc(result, c => c.Title); } - - private async Task> FillChapters(HtmlDocument doc, Uri uri) { + + private async Task> FillChapters(HtmlDocument doc, Uri uri) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var urlChapter in GetToc(doc, uri)) { - var chapter = new Chapter { + foreach (var urlChapter in GetToc(doc, uri)) + { + var chapter = new Chapter + { Title = urlChapter.Title }; Config.Logger.LogInformation($"Загружаю главу {urlChapter.Title.CoverQuotes()}"); var chapterDoc = await GetChapter(urlChapter.Url); - if (chapterDoc != default) { + if (chapterDoc != null) + { chapter.Images = await GetImages(chapterDoc, urlChapter.Url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } - + result.Add(chapter); } return result; } - private async Task GetChapter(Uri url) { + private async Task GetChapter(Uri url) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var content = doc.QuerySelector("div.entry-content"); var notice = content.QuerySelector("div.single-notice"); - return notice?.GetText() == "Для чтения купите главу." ? - default : + return notice?.GetText() == "Для чтения купите главу." ? + null : content.InnerHtml.AsHtmlDoc().RemoveNodes("div[class*=adv]"); } - private static Author GetAuthor() { + private static Author GetAuthor() + { return new Author("Ifreedom"); } - private Task GetCover(HtmlDocument doc, Uri uri) { + private Task GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("div.img-ranobe img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/GetterBase.cs b/src/Core/Logic/Getters/GetterBase.cs similarity index 69% rename from Core/Logic/Getters/GetterBase.cs rename to src/Core/Logic/Getters/GetterBase.cs index 26f9e93b..2bb5bd6d 100644 --- a/Core/Logic/Getters/GetterBase.cs +++ b/src/Core/Logic/Getters/GetterBase.cs @@ -1,12 +1,6 @@ -using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Net; -using System.Net.Http; using System.Text; -using System.Threading.Tasks; using System.Web; using Core.Configs; using Core.Extensions; @@ -17,162 +11,197 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public abstract class GetterBase(BookGetterConfig config) : IDisposable { +public abstract class GetterBase(BookGetterConfig config) : IDisposable +{ protected readonly BookGetterConfig Config = config; protected abstract Uri SystemUrl { get; } - protected virtual string GetId(Uri url) => url.Segments.Last().Trim('/'); + protected virtual string GetId(Uri url) + => url.Segments.Last().Trim('/'); - public virtual bool IsSameUrl(Uri url) { + public virtual bool IsSameUrl(Uri url) + { return SystemUrl.IsSameHost(url); } - protected virtual HttpRequestMessage GetImageRequestMessage(Uri uri) { + protected virtual HttpRequestMessage GetImageRequestMessage(Uri uri) + { var message = new HttpRequestMessage(HttpMethod.Get, uri); message.Version = Config.Client.DefaultRequestVersion; - foreach (var header in Config.Client.DefaultRequestHeaders) { + foreach (var header in Config.Client.DefaultRequestHeaders) + { message.Headers.TryAddWithoutValidation(header.Key, header.Value); } - + return message; } - + /// /// Получение изображения /// /// /// - protected async Task SaveImage(Uri uri) { - try { + protected async Task SaveImage(Uri uri) + { + try + { Config.Logger.LogInformation($"Загружаю картинку {uri}"); using var response = await Config.Client.SendWithTriesAsync(() => GetImageRequestMessage(uri)); Config.Logger.LogInformation($"Загружена картинка {response.RequestMessage!.RequestUri}"); - if (response is not { StatusCode: HttpStatusCode.OK }) { + if (response is not { StatusCode: HttpStatusCode.OK }) + { Console.WriteLine(response.StatusCode); - return default; + return null; } - + await using var stream = await response.Content.ReadAsStreamAsync(); var extension = Path.GetExtension(uri.GetFileName()); - var fullName = Guid.NewGuid() + (string.IsNullOrWhiteSpace(extension) ? ".jpg" : extension); - + var fullName = Guid.NewGuid() + (string.IsNullOrWhiteSpace(extension) ? ".jpg" : extension); + return await TempFile.Create(uri, Config.TempFolder.Path, fullName, stream); - } catch (Exception ex) { + } + catch (Exception ex) + { Console.WriteLine(ex); - return default; + return null; } } - + /// /// Загрузка изображений отдельной части /// /// /// - protected async Task> GetImages(HtmlDocument doc, Uri baseUri) { + protected async Task> GetImages(HtmlDocument doc, Uri baseUri) + { var images = new ConcurrentBag>(); var toRemove = new ConcurrentBag(); var tuples = doc.QuerySelectorAll("img, image").Select((img, i) => Tuple.Create(img, i)); - await Parallel.ForEachAsync(tuples, async (t, _) => { - var img = t.Item1; - - if (Config.Options.NoImage) { - toRemove.Add(img); - return; - } - - var path = img.Attributes["src"]?.Value ?? img.Attributes["data-src"]?.Value; - if (string.IsNullOrWhiteSpace(path)) { - toRemove.Add(img); - return; - } + await Parallel.ForEachAsync( + tuples, + async (t, _) => + { + var img = t.Item1; + + if (Config.Options.NoImage) + { + toRemove.Add(img); + return; + } - if (!Uri.TryCreate(baseUri, path, out var uri)) { - toRemove.Add(img); - return; - } + var path = img.Attributes["src"]?.Value ?? img.Attributes["data-src"]?.Value; + if (string.IsNullOrWhiteSpace(path)) + { + toRemove.Add(img); + return; + } - var image = await SaveImage(uri); - if (image == default) { - toRemove.Add(img); - return; - } + if (!Uri.TryCreate(baseUri, path, out var uri)) + { + toRemove.Add(img); + return; + } + + var image = await SaveImage(uri); + if (image == null) + { + toRemove.Add(img); + return; + } - img.Attributes.RemoveAll(); - img.Attributes.Add("src", image.FullName); - images.Add(Tuple.Create(image, t.Item2)); - }); + img.Attributes.RemoveAll(); + img.Attributes.Add("src", image.FullName); + images.Add(Tuple.Create(image, t.Item2)); + }); - foreach (var node in toRemove) { + foreach (var node in toRemove) + { node.Remove(); } return images.OrderBy(t => t.Item2).Select(t => t.Item1); } - public virtual Task Authorize() { + public virtual Task Authorize() + { return Task.CompletedTask; } - private static int GetIndexByName(ICollection toc, Func selector, string name) { + private static int GetIndexByName(ICollection toc, Func selector, string name) + { var result = toc - .Select((chapter, index) => new { chapter, index }) + .Select((chapter, index) => new + { + chapter, index + }) .FirstOrDefault(c => string.Equals(selector(c.chapter)?.Trim(), name, StringComparison.InvariantCultureIgnoreCase)) ?.index; - if (!result.HasValue) { + if (!result.HasValue) + { throw new Exception($"Глава с названием {name.CoverQuotes()} не найдена"); } - + return result.Value + 1; } - - private static HtmlDocument SliceBook(EpubBook epubBook, EpubChapter epubChapter) { + + private static HtmlDocument SliceBook(EpubBook epubBook, EpubChapter epubChapter) + { var doc = new HtmlDocument(); var startChapter = epubBook.Resources.Html.First(h => HttpUtility.UrlDecode(h.AbsolutePath) == HttpUtility.UrlDecode(epubChapter.AbsolutePath)); var startIndex = epubBook.Resources.Html.IndexOf(startChapter); - + var chapter = epubBook.Resources.Html[startIndex].TextContent.AsHtmlDoc(); - foreach (var node in chapter.QuerySelector("body").ChildNodes) { + foreach (var node in chapter.QuerySelector("body").ChildNodes) + { doc.DocumentNode.AppendChild(node); } - - for (var i = startIndex + 1; i < epubBook.Resources.Html.Count; i++) { + + for (var i = startIndex + 1; i < epubBook.Resources.Html.Count; i++) + { var chapterContent = epubBook.Resources.Html[i]; - if (chapterContent.AbsolutePath == epubChapter.Next?.AbsolutePath) { + if (chapterContent.AbsolutePath == epubChapter.Next?.AbsolutePath) + { break; } - + chapter = chapterContent.TextContent.AsHtmlDoc(); - foreach (var node in chapter.QuerySelector("body").ChildNodes) { + foreach (var node in chapter.QuerySelector("body").ChildNodes) + { doc.DocumentNode.AppendChild(node); } } - + return doc; } - - protected async Task> FillChaptersFromEpub(TempFile shortFile) { + + protected async Task> FillChaptersFromEpub(TempFile shortFile) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } await using var stream = shortFile.GetStream(); var epubBook = EpubReader.Read(stream, true, Encoding.UTF8); - if (epubBook.TableOfContents.Count > 0) { + if (epubBook.TableOfContents.Count > 0) + { var current = epubBook.TableOfContents.First(); - do { + do + { Config.Logger.LogInformation($"Загружаю главу {current.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = current.Title }; @@ -180,26 +209,30 @@ protected async Task> FillChaptersFromEpub(TempFile shortFi chapter.Images = await GetImages(content, epubBook); chapter.Content = content.DocumentNode.RemoveNodes("h1, h2, h3").InnerHtml; result.Add(chapter); - } while ((current = current.Next) != default); - } else { + } while ((current = current.Next) != null); + } + else + { Config.Logger.LogInformation($"Загружаю главу {epubBook.Title.CoverQuotes()}"); var sb = new StringBuilder(); - foreach (var textFile in epubBook.SpecialResources.HtmlInReadingOrder) { + foreach (var textFile in epubBook.SpecialResources.HtmlInReadingOrder) + { var textFileDoc = textFile.TextContent.AsHtmlDoc(); var body = textFileDoc.DocumentNode.QuerySelector("body"); - if (body != default) { + if (body != null) + { textFileDoc = body.InnerHtml.AsHtmlDoc(); } sb.Append(textFileDoc.DocumentNode.InnerHtml); - } - - var chapter = new Chapter { + + var chapter = new Chapter + { Title = epubBook.Title }; - + var content = sb.AsHtmlDoc(); chapter.Images = await GetImages(content, epubBook); chapter.Content = content.DocumentNode.InnerHtml; @@ -208,27 +241,32 @@ protected async Task> FillChaptersFromEpub(TempFile shortFi return result; } - - private async Task> GetImages(HtmlDocument doc, EpubBook book) { + + private async Task> GetImages(HtmlDocument doc, EpubBook book) + { var images = new List(); - foreach (var img in doc.QuerySelectorAll("img")) { + foreach (var img in doc.QuerySelectorAll("img")) + { var path = img.Attributes["src"]?.Value; - if (string.IsNullOrWhiteSpace(path)) { + if (string.IsNullOrWhiteSpace(path)) + { img.Remove(); continue; } var t = book.Resources.Images.FirstOrDefault(i => i.AbsolutePath.EndsWith(path.Trim('.'))); - if (t == default) { + if (t == null) + { img.Remove(); continue; } - if (t.Content == null || t.Content.Length == 0) { + if (t.Content == null || t.Content.Length == 0) + { img.Remove(); continue; } - + var image = await TempFile.Create(null, Config.TempFolder.Path, t.Href, t.Content); img.Attributes["src"].Value = image.FullName; images.Add(image); @@ -237,42 +275,49 @@ private async Task> GetImages(HtmlDocument doc, EpubBook b return images; } - protected IEnumerable SliceToc(ICollection toc, Func selector) { + protected IEnumerable SliceToc(ICollection toc, Func selector) + { var start = Config.Options.Start; var end = Config.Options.End; var startName = (Config.Options.StartName ?? string.Empty).Trim(); var endName = (Config.Options.EndName ?? string.Empty).Trim(); - if (!string.IsNullOrWhiteSpace(startName)) { + if (!string.IsNullOrWhiteSpace(startName)) + { start = GetIndexByName(toc, selector, startName); } - - if (!string.IsNullOrWhiteSpace(endName)) { + + if (!string.IsNullOrWhiteSpace(endName)) + { end = GetIndexByName(toc, selector, endName); } - if (start.HasValue && end.HasValue) { - var startIndex = start.Value >= 0 ? start.Value : toc.Count + start.Value + 1; - var endIndex = end.Value >= 0 ? end.Value : toc.Count + end.Value; - + if (start.HasValue && end.HasValue) + { + var startIndex = start.Value >= 0 ? start.Value : toc.Count + start.Value + 1; + var endIndex = end.Value >= 0 ? end.Value : toc.Count + end.Value; + return toc.Skip(startIndex - 1).Take(endIndex - startIndex + 1); } - - if (start.HasValue) { - var startIndex = start.Value >= 0 ? start.Value : toc.Count + start.Value + 1; + + if (start.HasValue) + { + var startIndex = start.Value >= 0 ? start.Value : toc.Count + start.Value + 1; return toc.Skip(startIndex - 1); } - - if (end.HasValue) { - var endIndex = end.Value >= 0 ? end.Value : toc.Count + end.Value; + + if (end.HasValue) + { + var endIndex = end.Value >= 0 ? end.Value : toc.Count + end.Value; return toc.Take(endIndex); } - + return toc; } - private static HtmlDocument GetContent(EpubBook epubBook, EpubChapter epubChapter) { + private static HtmlDocument GetContent(EpubBook epubBook, EpubChapter epubChapter) + { var chapter = new HtmlDocument(); var book = SliceBook(epubBook, epubChapter); @@ -280,55 +325,69 @@ private static HtmlDocument GetContent(EpubBook epubBook, EpubChapter epubChapte var needStop = false; var layer = (startNode.ParentNode ?? startNode).CloneNode(false); - do { + do + { var clone = CloneNode(startNode, epubChapter.Next?.HashLocation, ref needStop); - if (clone != default) { + if (clone != null) + { layer.AppendChild(clone); } - do { - if (startNode.NextSibling == default) { - if (startNode.ParentNode == default || startNode.ParentNode.Name == "#document") { - startNode = default; - } else { + do + { + if (startNode.NextSibling == null) + { + if (startNode.ParentNode == null || startNode.ParentNode.Name == "#document") + { + startNode = null; + } + else + { var layerClone = layer.CloneNode(true); layer = startNode.ParentNode.CloneNode(false); layer.AppendChild(layerClone); startNode = startNode.ParentNode; } - } else { + } + else + { startNode = startNode.NextSibling; break; } - } while (startNode != default); - } while (startNode != default && !needStop); + } while (startNode != null); + } while (startNode != null && !needStop); chapter.DocumentNode.AppendChild(layer); return chapter; } - private static HtmlNode CloneNode(HtmlNode node, string stopId, ref bool needStop) { - if (!string.IsNullOrWhiteSpace(stopId) && node.Name != "#document" && node.Id == stopId) { + private static HtmlNode CloneNode(HtmlNode node, string stopId, ref bool needStop) + { + if (!string.IsNullOrWhiteSpace(stopId) && node.Name != "#document" && node.Id == stopId) + { needStop = true; - return default; + return null; } - - if (!node.HasChildNodes) { + if (!node.HasChildNodes) + { return node.CloneNode(true); } var parent = node.CloneNode(false); - foreach (var child in node.ChildNodes) { + foreach (var child in node.ChildNodes) + { var clone = CloneNode(child, stopId, ref needStop); - if (clone != default) { + if (clone != null) + { parent.ChildNodes.Add(clone); } - if (needStop) { + if (needStop) + { return parent; } } @@ -337,18 +396,20 @@ private static HtmlNode CloneNode(HtmlNode node, string stopId, ref bool needSto } public abstract Task Get(Uri url); - - public virtual Task Init() { + + public virtual Task Init() + { Config.Client.DefaultRequestVersion = HttpVersion.Version20; Config.Client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15"); Config.Client.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); Config.Client.DefaultRequestHeaders.Add("Accept-Language", "ru"); Config.Client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate, br"); - + return Task.CompletedTask; } - public virtual void Dispose() { + public virtual void Dispose() + { Config?.Dispose(); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/HogwartsNetGetter.cs b/src/Core/Logic/Getters/HogwartsNetGetter.cs similarity index 70% rename from Core/Logic/Getters/HogwartsNetGetter.cs rename to src/Core/Logic/Getters/HogwartsNetGetter.cs index accbf4e4..965fdfbc 100644 --- a/Core/Logic/Getters/HogwartsNetGetter.cs +++ b/src/Core/Logic/Getters/HogwartsNetGetter.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net.Http; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -12,130 +7,152 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class HogwartsNetGetter(BookGetterConfig config) : GetterBase(config) { +public class HogwartsNetGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://hogwartsnet.ru/"); - + private static Encoding _encoding = Encoding.GetEncoding(1251); - protected override string GetId(Uri url) => url.GetQueryParameter("fid"); + protected override string GetId(Uri url) + => url.GetQueryParameter("fid"); - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } - var payload = new Dictionary { - ["id"] = Config.Options.Login, - ["pwd"] = Config.Options.Password + var payload = new Dictionary + { + ["id"] = Config.Options.Login, ["pwd"] = Config.Options.Password }; var doc = await Config.Client.PostHtmlDocWithTriesAsync("https://hogwartsnet.ru/mfanf/member.php".AsUri(), new FormUrlEncodedContent(payload), _encoding); var anketa = doc.QuerySelector("tr.top_fanf a[href*=anketa.php]"); - - if (anketa == default) { + + if (anketa == null) + { throw new Exception("Не удалось авторизоваться. Проверьте правильность ID/пароля"); } Config.Logger.LogInformation("Успешно авторизовались"); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = $"https://hogwartsnet.ru/mfanf/ffshowfic.php?fid={GetId(url)}".AsUri(); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url, _encoding); - var book = new Book(url) { + var book = new Book(url) + { Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("td.fichead a[href*=ffshowfic.php]"), Author = GetAuthor(doc, url), Annotation = doc.QuerySelector("td.fichead div")?.InnerHtml, Seria = GetSeria(doc, url) }; - + return book; } - private async Task> FillChapters(HtmlDocument doc, Uri url) { + private async Task> FillChapters(HtmlDocument doc, Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var urlChapter in GetToc(doc, url)) { + foreach (var urlChapter in GetToc(doc, url)) + { Config.Logger.LogInformation($"Загружаю главу {urlChapter.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = urlChapter.Title }; var chapterDoc = await GetChapter(urlChapter.Url); - if (chapterDoc != default) { + if (chapterDoc != null) + { chapter.Images = await GetImages(chapterDoc, urlChapter.Url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } - + result.Add(chapter); } return result; } - private async Task GetChapter(Uri url) { + private async Task GetChapter(Uri url) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(url, _encoding); const string mature = "Текст фанфика доступен только зарегистрированным пользователям старше 18 лет"; - if (doc.ParsedText.Contains(mature)) { + if (doc.ParsedText.Contains(mature)) + { throw new Exception($"{mature}. Добавьте авторизацию по ID и паролю."); } var text = new StringBuilder(); using var sr = new StringReader(doc.QuerySelector("#chap_text").RemoveNodes("center, div[id*=yandex]").InnerText.HtmlDecode()); - while (true) { + while (true) + { var line = await sr.ReadLineAsync(); - if (line == null) { + if (line == null) + { break; } - if (string.IsNullOrWhiteSpace(line)) { + if (string.IsNullOrWhiteSpace(line)) + { continue; } - + text.Append(line.HtmlEncode().CoverTag("p")); } return text.AsHtmlDoc(); } - private IEnumerable GetToc(HtmlDocument doc, Uri url) { + private IEnumerable GetToc(HtmlDocument doc, Uri url) + { var result = new List(); - - foreach (var option in doc.QuerySelectorAll("td.fichead_chapters select[name=chapter] option")) { + + foreach (var option in doc.QuerySelectorAll("td.fichead_chapters select[name=chapter] option")) + { var chapterId = option.Attributes["value"].Value; var chapterUrl = url.AppendQueryParameter("chapter", chapterId); var chapterTitle = option.GetText().StartsWith("глава") ? $"Глава {chapterId}" : option.GetText(); - + result.Add(new UrlChapter(chapterUrl, chapterTitle)); } return SliceToc(result, c => c.Title); } - private static Seria GetSeria(HtmlDocument doc, Uri url) { + private static Seria GetSeria(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("td.fichead a[href*=findex.php]"); - if (a != default) { - return new Seria { - Name = a.GetText(), - Url = url.MakeRelativeUri(a.Attributes["href"].Value) + if (a != null) + { + return new Seria + { + Name = a.GetText(), Url = url.MakeRelativeUri(a.Attributes["href"].Value) }; } - return default; + return null; } - private static Author GetAuthor(HtmlDocument doc, Uri url) { + private static Author GetAuthor(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("td.fichead a[href*=member.php]"); - return a != default ? - new Author(a.GetText().ReplaceNewLine(), url.MakeRelativeUri(a.Attributes["href"].Value)) : + return a != null ? + new Author(a.GetText().ReplaceNewLine(), url.MakeRelativeUri(a.Attributes["href"].Value)) : new Author("HogwartsNet"); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/HotNovelPub/EzNovelsGetter.cs b/src/Core/Logic/Getters/HotNovelPub/EzNovelsGetter.cs similarity index 71% rename from Core/Logic/Getters/HotNovelPub/EzNovelsGetter.cs rename to src/Core/Logic/Getters/HotNovelPub/EzNovelsGetter.cs index badb19e9..9f81dcc2 100644 --- a/Core/Logic/Getters/HotNovelPub/EzNovelsGetter.cs +++ b/src/Core/Logic/Getters/HotNovelPub/EzNovelsGetter.cs @@ -1,9 +1,9 @@ -using System; using Core.Configs; -namespace Core.Logic.Getters.HotNovelPub; +namespace Core.Logic.Getters.HotNovelPub; -public class EzNovelsGetter(BookGetterConfig config) : HotNovelPubGetterBase(config) { +public class EzNovelsGetter(BookGetterConfig config) : HotNovelPubGetterBase(config) +{ protected override Uri SystemUrl => new("https://eznovels.com"); protected override string Lang => "ru"; -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/HotNovelPub/HotNovelPubGetter.cs b/src/Core/Logic/Getters/HotNovelPub/HotNovelPubGetter.cs similarity index 70% rename from Core/Logic/Getters/HotNovelPub/HotNovelPubGetter.cs rename to src/Core/Logic/Getters/HotNovelPub/HotNovelPubGetter.cs index 5df959e3..b76bda25 100644 --- a/Core/Logic/Getters/HotNovelPub/HotNovelPubGetter.cs +++ b/src/Core/Logic/Getters/HotNovelPub/HotNovelPubGetter.cs @@ -1,9 +1,9 @@ -using System; using Core.Configs; -namespace Core.Logic.Getters.HotNovelPub; +namespace Core.Logic.Getters.HotNovelPub; -public class HotNovelPubGetter(BookGetterConfig config) : HotNovelPubGetterBase(config) { +public class HotNovelPubGetter(BookGetterConfig config) : HotNovelPubGetterBase(config) +{ protected override Uri SystemUrl => new("https://hotnovelpub.com/"); protected override string Lang => "en"; -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/HotNovelPub/HotNovelPubGetterBase.cs b/src/Core/Logic/Getters/HotNovelPub/HotNovelPubGetterBase.cs similarity index 79% rename from Core/Logic/Getters/HotNovelPub/HotNovelPubGetterBase.cs rename to src/Core/Logic/Getters/HotNovelPub/HotNovelPubGetterBase.cs index 174a2568..36899ae3 100644 --- a/Core/Logic/Getters/HotNovelPub/HotNovelPubGetterBase.cs +++ b/src/Core/Logic/Getters/HotNovelPub/HotNovelPubGetterBase.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; using System.Net.Http.Json; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -12,25 +9,29 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.HotNovelPub; +namespace Core.Logic.Getters.HotNovelPub; -public abstract class HotNovelPubGetterBase(BookGetterConfig config) : GetterBase(config) { +public abstract class HotNovelPubGetterBase(BookGetterConfig config) : GetterBase(config) +{ protected abstract string Lang { get; } private Uri _apiUrl => new($"https://api.{SystemUrl.Host}/"); - public override Task Init() { + public override Task Init() + { base.Init(); Config.Client.DefaultRequestHeaders.Add("lang", Lang); Config.Client.DefaultRequestHeaders.Add("Priority", "u=3, i"); return Task.CompletedTask; } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = await GetMainUrl(url); var bookApi = await GetBook(GetId(url)); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(bookApi.Book), Chapters = await FillChapters(bookApi.Chapters), Title = bookApi.Book.Name, @@ -42,27 +43,33 @@ public override async Task Get(Uri url) { return book; } - private async Task GetMainUrl(Uri url) { - if (GetId(url).StartsWith("chapter-")) { + private async Task GetMainUrl(Uri url) + { + if (GetId(url).StartsWith("chapter-")) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var a = doc.QuerySelectorAll("li.breadcrumb-item")[1].QuerySelector("a[href]"); url = SystemUrl.MakeRelativeUri(a.Attributes["href"].Value); } - + return url; } - private async Task> FillChapters(ICollection toc) { + private async Task> FillChapters(ICollection toc) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - - foreach (var ezChapter in SliceToc(toc, c => c.Title)) { + + foreach (var ezChapter in SliceToc(toc, c => c.Title)) + { var title = ezChapter.Title.Trim(); Config.Logger.LogInformation($"Загружаю главу {title.CoverQuotes()}"); - - var chapter = new Chapter { + + var chapter = new Chapter + { Title = title }; @@ -72,20 +79,22 @@ private async Task> FillChapters(ICollection GetChapter(HotNovelPubChapter ezChapter) { + private async Task GetChapter(HotNovelPubChapter ezChapter) + { var doc = Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri(ezChapter.Slug)); var additional = Config.Client.GetFromJsonAsync>(SystemUrl.MakeRelativeUri($"/server/getContent?slug={ezChapter.Slug}")); - + const string watermark = ".copy right hot novel pub"; var sb = new StringBuilder((await doc).QuerySelector("#content-item").InnerHtml.HtmlDecode().CleanInvalidXmlChars().Replace(watermark, string.Empty)); - - foreach (var row in (await additional)!.Data) { - foreach (var p in row.Split("\n", StringSplitOptions.RemoveEmptyEntries)) { + foreach (var row in (await additional)!.Data) + { + foreach (var p in row.Split("\n", StringSplitOptions.RemoveEmptyEntries)) + { sb.Append(p.HtmlDecode().CleanInvalidXmlChars().Replace(watermark, string.Empty).Trim().CoverTag("p")); } } @@ -93,16 +102,19 @@ private async Task GetChapter(HotNovelPubChapter ezChapter) { return sb.AsHtmlDoc(); } - private Author GetAuthor(HotNovelPubBook book) { + private Author GetAuthor(HotNovelPubBook book) + { return new Author(book.Authorize.Name, SystemUrl.MakeRelativeUri(book.Authorize.Slug)); } - private Task GetCover(HotNovelPubBook book) { + private Task GetCover(HotNovelPubBook book) + { return !string.IsNullOrWhiteSpace(book.Image) ? SaveImage(SystemUrl.MakeRelativeUri(book.Image)) : Task.FromResult(default(TempFile)); } - private async Task GetBook(string id) { + private async Task GetBook(string id) + { var response = await Config.Client.GetFromJsonAsync>(_apiUrl.MakeRelativeUri($"/book/{id}")); return response!.Data; } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/HotNovelPub/LaNovelsGetter.cs b/src/Core/Logic/Getters/HotNovelPub/LaNovelsGetter.cs similarity index 71% rename from Core/Logic/Getters/HotNovelPub/LaNovelsGetter.cs rename to src/Core/Logic/Getters/HotNovelPub/LaNovelsGetter.cs index 18c38533..0a175590 100644 --- a/Core/Logic/Getters/HotNovelPub/LaNovelsGetter.cs +++ b/src/Core/Logic/Getters/HotNovelPub/LaNovelsGetter.cs @@ -1,9 +1,9 @@ -using System; using Core.Configs; -namespace Core.Logic.Getters.HotNovelPub; +namespace Core.Logic.Getters.HotNovelPub; -public class LaNovelsGetter(BookGetterConfig config) : HotNovelPubGetterBase(config) { +public class LaNovelsGetter(BookGetterConfig config) : HotNovelPubGetterBase(config) +{ protected override Uri SystemUrl => new("https://lanovels.com/"); protected override string Lang => "pt"; -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/HotNovelPub/LightNovelDailyGetter.cs b/src/Core/Logic/Getters/HotNovelPub/LightNovelDailyGetter.cs similarity index 70% rename from Core/Logic/Getters/HotNovelPub/LightNovelDailyGetter.cs rename to src/Core/Logic/Getters/HotNovelPub/LightNovelDailyGetter.cs index 027569de..c4c62734 100644 --- a/Core/Logic/Getters/HotNovelPub/LightNovelDailyGetter.cs +++ b/src/Core/Logic/Getters/HotNovelPub/LightNovelDailyGetter.cs @@ -1,9 +1,9 @@ -using System; using Core.Configs; -namespace Core.Logic.Getters.HotNovelPub; +namespace Core.Logic.Getters.HotNovelPub; -public class LightNovelDailyGetter(BookGetterConfig config) : HotNovelPubGetterBase(config) { +public class LightNovelDailyGetter(BookGetterConfig config) : HotNovelPubGetterBase(config) +{ protected override Uri SystemUrl => new("https://lightnoveldaily.com"); protected override string Lang => "es"; -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/HubBookGetter.cs b/src/Core/Logic/Getters/HubBookGetter.cs similarity index 77% rename from Core/Logic/Getters/HubBookGetter.cs rename to src/Core/Logic/Getters/HubBookGetter.cs index 7fc09c10..23e5fabc 100644 --- a/Core/Logic/Getters/HubBookGetter.cs +++ b/src/Core/Logic/Getters/HubBookGetter.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -10,40 +7,47 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class HubBookGetter(BookGetterConfig config) : GetterBase(config) { +public class HubBookGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://hub-book.com/"); - protected override string GetId(Uri url) => url.GetSegment(2); + protected override string GetId(Uri url) + => url.GetSegment(2); - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri($"/books/{GetId(url)}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var author = GetAuthor(doc, url); var title = doc.GetTextBySelector("h1").Replace(author.Name, string.Empty).Trim('-', ' '); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url, title), Title = title, Author = author, Annotation = doc.QuerySelector("div.b-book-desc div.more_text")?.InnerHtml, }; - + return book; } - private async Task> FillChapters(HtmlDocument doc, Uri url, string title) { - if (Config.Options.NoChapters) { + private async Task> FillChapters(HtmlDocument doc, Uri url, string title) + { + if (Config.Options.NoChapters) + { return []; } - + var pages = int.Parse(doc.GetTextBySelector("span[itemprop=numberOfPages]")); var text = new StringBuilder(); - for (var i = 1; i <= pages; i++) { + for (var i = 1; i <= pages; i++) + { Config.Logger.LogInformation($"Получаю страницу {i}/{pages}"); doc = await Config.Client.GetHtmlDocWithTriesAsync(url.AppendSegment($"/toread/page-{i}")); text.Append(doc.QuerySelector("div.b-reader-text__container").InnerHtml.HtmlDecode()); @@ -51,21 +55,26 @@ private async Task> FillChapters(HtmlDocument doc, Uri url, var chapter = new Chapter(); var chapterDoc = text.AsHtmlDoc(); - + chapter.Title = title; chapter.Images = await GetImages(chapterDoc, url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; - return new[] { chapter }; + return new[] + { + chapter + }; } - private Task GetCover(HtmlDocument doc, Uri uri) { + private Task GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("div.b-book-image img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - - private static Author GetAuthor(HtmlDocument doc, Uri uri) { + + private static Author GetAuthor(HtmlDocument doc, Uri uri) + { var a = doc.QuerySelector("a.b-book-user__name"); return new Author(a.GetText(), uri.MakeRelativeUri(a.Attributes["href"].Value)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/IGramGetter.cs b/src/Core/Logic/Getters/IGramGetter.cs similarity index 70% rename from Core/Logic/Getters/IGramGetter.cs rename to src/Core/Logic/Getters/IGramGetter.cs index b8f30561..561a3594 100644 --- a/Core/Logic/Getters/IGramGetter.cs +++ b/src/Core/Logic/Getters/IGramGetter.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; using System.Net.Http.Json; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -14,47 +9,55 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class IGramGetter(BookGetterConfig config) : GetterBase(config) { +public class IGramGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://i-gram.ru/"); - protected override string GetId(Uri url) => url.GetSegment(2); + protected override string GetId(Uri url) + => url.GetSegment(2); - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } var doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri("/account/login/")); var token = doc.QuerySelector("input[name=csrfmiddlewaretoken]")?.Attributes["value"]?.Value; - + Config.Client.DefaultRequestHeaders.Add("Referer", SystemUrl.MakeRelativeUri("/account/login/").ToString()); using var post = await Config.Client.PostAsync(SystemUrl.MakeRelativeUri("/account/login/"), GenerateAuthData(token)); var checkLogin = await Config.Client.GetFromJsonAsync(SystemUrl.MakeRelativeUri("/api/v2/users/current/")); - if (checkLogin?.Id > 0) { + if (checkLogin?.Id > 0) + { Config.Logger.LogInformation("Успешно авторизовались"); - } else { + } + else + { throw new Exception("Не удалось авторизоваться"); } } - private FormUrlEncodedContent GenerateAuthData(string token) { - var data = new Dictionary { - ["csrfmiddlewaretoken"] = token, - ["remember"] = "checked", - ["login"] = Config.Options.Login, - ["password"] = Config.Options.Password + private FormUrlEncodedContent GenerateAuthData(string token) + { + var data = new Dictionary + { + ["csrfmiddlewaretoken"] = token, ["remember"] = "checked", ["login"] = Config.Options.Login, ["password"] = Config.Options.Password }; return new FormUrlEncodedContent(data); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri($"/b/{GetId(url)}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - - var book = new Book(url) { + + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(url, doc), Title = doc.GetTextBySelector("h1[itemprop=name]"), @@ -62,64 +65,75 @@ public override async Task Get(Uri url) { Annotation = doc.QuerySelector("div[itemprop=description] p")?.InnerHtml, Seria = GetSeria(doc, url) }; - + return book; } - private static Seria GetSeria(HtmlDocument doc, Uri url) { + private static Seria GetSeria(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("div.bk-genre a[href^=/b/cycle/]"); - if (a != default) { - return new Seria { - Name = a.GetText(), - Url = url.MakeRelativeUri(a.Attributes["href"].Value) + if (a != null) + { + return new Seria + { + Name = a.GetText(), Url = url.MakeRelativeUri(a.Attributes["href"].Value) }; } - return default; + return null; } - private async Task> FillChapters(Uri uri, HtmlDocument doc) { + private async Task> FillChapters(Uri uri, HtmlDocument doc) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var bookChapter in await GetToc(doc)) { + foreach (var bookChapter in await GetToc(doc)) + { Config.Logger.LogInformation($"Загружаю главу {bookChapter.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = bookChapter.Title }; - - if (!string.IsNullOrWhiteSpace(bookChapter.Text)) { + + if (!string.IsNullOrWhiteSpace(bookChapter.Text)) + { var chapterDoc = bookChapter.Text.AsHtmlDoc(); chapter.Images = await GetImages(chapterDoc, uri); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } - + result.Add(chapter); } return result; } - private async Task> GetToc(HtmlDocument doc) { + private async Task> GetToc(HtmlDocument doc) + { var response = await Config.Client.GetAsync(SystemUrl.MakeRelativeUri($"/api/v1/book/{GetInternalId(doc)}/chapters")); var content = await response.Content.ReadAsStringAsync(); return SliceToc(content.Deserialize>().OrderBy(c => c.Id).ToList(), c => c.Title); } - private static Author GetAuthor(HtmlDocument doc, Uri uri) { + private static Author GetAuthor(HtmlDocument doc, Uri uri) + { var a = doc.QuerySelector("div.bk-author a"); return new Author(a.GetText(), uri.MakeRelativeUri(a.Attributes["href"].Value)); } - private static string GetInternalId(HtmlDocument doc) { + private static string GetInternalId(HtmlDocument doc) + { return Regex.Match(doc.DocumentNode.InnerHtml, @"bookId: '(?\d+)'").Groups["id"].Value; } - private Task GetCover(HtmlDocument doc, Uri uri) { + private Task GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("div.bk-img img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/JaomixGetter.cs b/src/Core/Logic/Getters/JaomixGetter.cs similarity index 74% rename from Core/Logic/Getters/JaomixGetter.cs rename to src/Core/Logic/Getters/JaomixGetter.cs index 34d947d3..dbfc0988 100644 --- a/Core/Logic/Getters/JaomixGetter.cs +++ b/src/Core/Logic/Getters/JaomixGetter.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -11,34 +7,38 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class JaomixGetter(BookGetterConfig config) : GetterBase(config) { +public class JaomixGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://jaomix.ru/"); - protected override string GetId(Uri url) => url.GetSegment(1); + protected override string GetId(Uri url) + => url.GetSegment(1); - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri($"/{GetId(url)}/"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { - Cover = await GetCover(doc, url), - Chapters = await FillChapters(doc, url), - Title = doc.GetTextBySelector("h1"), - Author = new Author("Jaomix") + var book = new Book(url) + { + Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("h1"), Author = new Author("Jaomix") }; - + return book; } - - private async Task> FillChapters(HtmlDocument doc, Uri url) { + + private async Task> FillChapters(HtmlDocument doc, Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var jaomixChapter in GetToc(doc, url)) { + foreach (var jaomixChapter in GetToc(doc, url)) + { Config.Logger.LogInformation($"Загружаю главу {jaomixChapter.Title.CoverQuotes()}"); var chapter = new Chapter(); var chapterDoc = await GetChapter(jaomixChapter.Url); @@ -53,18 +53,22 @@ private async Task> FillChapters(HtmlDocument doc, Uri url) return result; } - private async Task GetChapter(Uri url) { + private async Task GetChapter(Uri url) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - while (doc.QuerySelector("div.themeform div.h-captcha, div.themeform div.but-captcha") != null) { + while (doc.QuerySelector("div.themeform div.h-captcha, div.themeform div.but-captcha") != null) + { Config.Logger.LogInformation($"Обнаружена капча. Перейдите по ссылке {url}, введите капчу и нажмите Enter..."); Console.Read(); doc = await Config.Client.GetHtmlDocWithTriesAsync(url); } - + var sb = new StringBuilder(); - - foreach (var node in doc.QuerySelector("div.themeform").ChildNodes) { - if (node.Name != "br" && node.Name != "script" && !string.IsNullOrWhiteSpace(node.InnerHtml) && node.Attributes["class"]?.Value?.Contains("adblock-service") == null) { + + foreach (var node in doc.QuerySelector("div.themeform").ChildNodes) + { + if (node.Name != "br" && node.Name != "script" && !string.IsNullOrWhiteSpace(node.InnerHtml) && node.Attributes["class"]?.Value?.Contains("adblock-service") == null) + { var tag = node.Name == "#text" ? "p" : node.Name; sb.Append(node.InnerHtml.HtmlDecode().CoverTag(tag)); } @@ -73,19 +77,23 @@ private async Task GetChapter(Uri url) { return sb.AsHtmlDoc(); } - private IEnumerable GetToc(HtmlDocument doc, Uri url) { + private IEnumerable GetToc(HtmlDocument doc, Uri url) + { var chapters = ParseChapters(doc, url).ToList(); Config.Logger.LogInformation($"Получено {chapters.Count} глав"); - + return SliceToc(chapters, c => c.Title); } - - private Task GetCover(HtmlDocument doc, Uri bookUri) { + + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("div.img-book img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - private static IEnumerable ParseChapters(HtmlDocument doc, Uri url) { - return doc.QuerySelectorAll("form.download-chapter .hiddenstab .flex-dow-txt a").Select(a => new UrlChapter(url.MakeRelativeUri(a.Attributes["href"].Value), a.InnerText.Trim())).Reverse(); + private static IEnumerable ParseChapters(HtmlDocument doc, Uri url) + { + return doc.QuerySelectorAll("form.download-chapter .hiddenstab .flex-dow-txt a").Select(a => new UrlChapter(url.MakeRelativeUri(a.Attributes["href"].Value), a.InnerText.Trim())) + .Reverse(); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/LibSocial/HentaiLibGetter.cs b/src/Core/Logic/Getters/LibSocial/HentaiLibGetter.cs similarity index 88% rename from Core/Logic/Getters/LibSocial/HentaiLibGetter.cs rename to src/Core/Logic/Getters/LibSocial/HentaiLibGetter.cs index bc788384..15ce779c 100644 --- a/Core/Logic/Getters/LibSocial/HentaiLibGetter.cs +++ b/src/Core/Logic/Getters/LibSocial/HentaiLibGetter.cs @@ -1,11 +1,11 @@ -using System; using Core.Configs; namespace Core.Logic.Getters.LibSocial; -public class HentaiLibGetter(BookGetterConfig config) : MangaLibGetterBase(config) { +public class HentaiLibGetter(BookGetterConfig config) : MangaLibGetterBase(config) +{ protected override Uri ImagesHost => new("https://img3.imglib.info/"); - + protected override Uri SystemUrl => new("https://hentailib.me/"); protected override int SiteId => 4; diff --git a/Core/Logic/Getters/LibSocial/MangaLibGetter.cs b/src/Core/Logic/Getters/LibSocial/MangaLibGetter.cs similarity index 90% rename from Core/Logic/Getters/LibSocial/MangaLibGetter.cs rename to src/Core/Logic/Getters/LibSocial/MangaLibGetter.cs index 0c4c3d1b..0e7d47c2 100644 --- a/Core/Logic/Getters/LibSocial/MangaLibGetter.cs +++ b/src/Core/Logic/Getters/LibSocial/MangaLibGetter.cs @@ -1,9 +1,9 @@ -using System; using Core.Configs; namespace Core.Logic.Getters.LibSocial; -public class MangaLibGetter(BookGetterConfig config) : MangaLibGetterBase(config) { +public class MangaLibGetter(BookGetterConfig config) : MangaLibGetterBase(config) +{ protected override Uri ImagesHost => new("https://img33.imgslib.link/"); protected override Uri SystemUrl => new("https://mangalib.me/"); diff --git a/Core/Logic/Getters/LibSocial/MangaLibGetterBase.cs b/src/Core/Logic/Getters/LibSocial/MangaLibGetterBase.cs similarity index 72% rename from Core/Logic/Getters/LibSocial/MangaLibGetterBase.cs rename to src/Core/Logic/Getters/LibSocial/MangaLibGetterBase.cs index 5a0d056e..514375a0 100644 --- a/Core/Logic/Getters/LibSocial/MangaLibGetterBase.cs +++ b/src/Core/Logic/Getters/LibSocial/MangaLibGetterBase.cs @@ -4,17 +4,20 @@ using Core.Types.SocialLib; using HtmlAgilityPack; -namespace Core.Logic.Getters.LibSocial; +namespace Core.Logic.Getters.LibSocial; -public abstract class MangaLibGetterBase(BookGetterConfig config) : NewLibSocialGetterBase(config) { - protected override HtmlDocument ResponseToHtmlDoc(SocialLibBookChapter chapterResponse) { +public abstract class MangaLibGetterBase(BookGetterConfig config) : NewLibSocialGetterBase(config) +{ + protected override HtmlDocument ResponseToHtmlDoc(SocialLibBookChapter chapterResponse) + { var sb = new StringBuilder(); - foreach (var page in chapterResponse.Pages) { + foreach (var page in chapterResponse.Pages) + { var url = SystemUrl.MakeRelativeUri(page.Url.TrimStart('/')); sb.Append($""); } return sb.AsHtmlDoc(); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/LibSocial/MangaLibOrgGetter.cs b/src/Core/Logic/Getters/LibSocial/MangaLibOrgGetter.cs similarity index 89% rename from Core/Logic/Getters/LibSocial/MangaLibOrgGetter.cs rename to src/Core/Logic/Getters/LibSocial/MangaLibOrgGetter.cs index 0bdd8ce6..4d548c5a 100644 --- a/Core/Logic/Getters/LibSocial/MangaLibOrgGetter.cs +++ b/src/Core/Logic/Getters/LibSocial/MangaLibOrgGetter.cs @@ -1,9 +1,9 @@ -using System; using Core.Configs; namespace Core.Logic.Getters.LibSocial; -public class MangaLibOrgGetter(BookGetterConfig config) : MangaLibGetterBase(config) { +public class MangaLibOrgGetter(BookGetterConfig config) : MangaLibGetterBase(config) +{ protected override Uri ImagesHost => new("https://img33.imgslib.link/"); protected override Uri SystemUrl => new("https://mangalib.org/"); diff --git a/Core/Logic/Getters/LibSocial/NewLibSocialGetterBase.cs b/src/Core/Logic/Getters/LibSocial/NewLibSocialGetterBase.cs similarity index 76% rename from Core/Logic/Getters/LibSocial/NewLibSocialGetterBase.cs rename to src/Core/Logic/Getters/LibSocial/NewLibSocialGetterBase.cs index d4896844..45b2963b 100644 --- a/Core/Logic/Getters/LibSocial/NewLibSocialGetterBase.cs +++ b/src/Core/Logic/Getters/LibSocial/NewLibSocialGetterBase.cs @@ -1,13 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Net.Http; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Security.Cryptography; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -19,13 +14,14 @@ namespace Core.Logic.Getters.LibSocial; -public abstract class NewLibSocialGetterBase(BookGetterConfig config) : GetterBase(config) { +public abstract class NewLibSocialGetterBase(BookGetterConfig config) : GetterBase(config) +{ private static Uri AuthHost => new("https://auth.lib.social/"); private static Uri ApiHost => new("https://api.cdnlibs.org/"); private static Uri BaseImageHost => new("https://cover.imglib.info/"); - + protected virtual Uri ImagesHost => BaseImageHost; private const string ALPHABET_BASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; @@ -33,134 +29,173 @@ public abstract class NewLibSocialGetterBase(BookGetterConfig config) : GetterBa protected abstract int SiteId { get; } - public override async Task Init() { + public override async Task Init() + { await base.Init(); Config.Client.DefaultRequestHeaders.Remove("Site-Id"); Config.Client.DefaultRequestHeaders.Add("Site-Id", SiteId.ToString()); - try { + try + { var timeZoneId = TimeZoneInfo.Local.Id; Config.Client.DefaultRequestHeaders.Remove("Client-Time-Zone"); Config.Client.DefaultRequestHeaders.Add("Client-Time-Zone", timeZoneId); - } catch (TimeZoneNotFoundException) { + } + catch (TimeZoneNotFoundException) + { // Игнорируем: таймзона влияет только на аналитические метрики сайта - } catch (InvalidTimeZoneException) { + } + catch (InvalidTimeZoneException) + { // Игнорируем: таймзона влияет только на аналитические метрики сайта } } - private static string TrimForLog(string value, int maxLength = 300) { - if (string.IsNullOrWhiteSpace(value)) { + private static string TrimForLog(string value, int maxLength = 300) + { + if (string.IsNullOrWhiteSpace(value)) + { return string.Empty; } var trimmed = value.Trim(); return trimmed.Length <= maxLength ? trimmed : trimmed[..maxLength] + "…"; } - - private static string Challenge(string str) { + + private static string Challenge(string str) + { var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(str).AsSpan(0, Encoding.UTF8.GetByteCount(str))); var result = string.Empty; - - for (var t = 0; t < bytes.Length; t += 3) { + + for (var t = 0; t < bytes.Length; t += 3) + { result += ALPHABET_CHALLENGE[bytes[t] >> 2]; result += ALPHABET_CHALLENGE[(bytes[t] & 3) << 4 | bytes[t + 1] >> 4]; result += ALPHABET_CHALLENGE[(bytes[t + 1] & 15) << 2 | (t + 2 < bytes.Length ? bytes[t + 2] : 0) >> 6]; result += ALPHABET_CHALLENGE[(t + 2 < bytes.Length ? bytes[t + 2] : 0) & 63]; } - return (bytes.Length % 3) switch { + return (bytes.Length % 3) switch + { 2 => result[..^1], 1 => result[..^2], _ => result }; } - private static string GetRandom(int length) { + private static string GetRandom(int length) + { return Enumerable .Repeat(0, length) .Aggregate(string.Empty, (c, _) => c + ALPHABET_BASE[new Random().Next(ALPHABET_BASE.Length)]); } - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } var secret = GetRandom(128); var state = GetRandom(40); var redirectUri = SystemUrl.MakeRelativeUri("/ru/front/auth/oauth/callback"); - + var challenge = Challenge(secret); - var challengeUrl = AuthHost.MakeRelativeUri($"/auth/oauth/authorize?scope=&client_id=1&response_type=code&redirect_uri={redirectUri}&state={state}&code_challenge={challenge}&code_challenge_method=S256&prompt=consent"); - + var challengeUrl = AuthHost.MakeRelativeUri( + $"/auth/oauth/authorize?scope=&client_id=1&response_type=code&redirect_uri={redirectUri}&state={state}&code_challenge={challenge}&code_challenge_method=S256&prompt=consent"); + var loginForm = await Config.Client.GetHtmlDocWithTriesAsync(challengeUrl); var tokenInput = loginForm.QuerySelector("input[name=_token]"); - if (tokenInput == default) { + if (tokenInput == null) + { throw new Exception("Не удалось получить csrf токен формы авторизации сайта (возможно, заблокировал DDoS-Guard)"); } - var payload = new Dictionary { - { "_token", tokenInput.Attributes["value"].Value }, - { "login", Config.Options.Login }, - { "password", Config.Options.Password }, + var payload = new Dictionary + { + { + "_token", tokenInput.Attributes["value"].Value + }, + { + "login", Config.Options.Login + }, + { + "password", Config.Options.Password + }, }; await Config.Client.PostHtmlDocWithTriesAsync(AuthHost.MakeRelativeUri("/auth/login"), new FormUrlEncodedContent(payload)); var login = await Config.Client.GetHtmlDocWithTriesAsync(challengeUrl); - if (login.QuerySelector(".g-recaptcha") != default) { + if (login.QuerySelector(".g-recaptcha") != null) + { throw new Exception("Авторизация заблокирована капчей. Укажите --flare с адресом FlareSolverr"); } + var error = login.QuerySelector(".form-field__error"); - if (error != default && !string.IsNullOrWhiteSpace(error.InnerText)) { + if (error != null && !string.IsNullOrWhiteSpace(error.InnerText)) + { throw new Exception($"Не удалось авторизоваться. {error.InnerText.Trim()}"); } var postForm = login.QuerySelector("form[method=post]"); - if (postForm == default) { + if (postForm == null) + { throw new Exception("Не удалось получить форму подтверждения OAuth на сайте (проверьте логин/пароль)"); } payload = postForm .QuerySelectorAll("input[type=hidden]") .ToDictionary(input => input.Attributes["name"].Value, input => input.Attributes["value"].Value); - + using var authorize = await Config.Client.PostAsync(AuthHost.MakeRelativeUri(postForm.Attributes["action"].Value), new FormUrlEncodedContent(payload)); - if ((int)authorize.StatusCode == 419) { + if ((int)authorize.StatusCode == 419) + { throw new Exception("Авторизация отклонена (код 419). Обычно это означает истекший CSRF токен или необходимость пройти капчу. Попробуйте указать --flare"); } - if (authorize == default || authorize.RequestMessage?.RequestUri == default) { + + if (authorize == null || authorize.RequestMessage?.RequestUri == null) + { throw new Exception("Сайт не вернул код авторизации (проверьте логин/пароль)"); } var authCode = authorize.RequestMessage.RequestUri.GetQueryParameter("code"); - if (string.IsNullOrWhiteSpace(authCode)) { + if (string.IsNullOrWhiteSpace(authCode)) + { throw new Exception("Сайт не вернул code после авторизации (проверьте логин/пароль)"); } - var tokenResponse = await Config.Client.PostAsync(ApiHost.MakeRelativeUri("/api/auth/oauth/token"), JsonContent.Create(new { - grant_type = "authorization_code", - client_id = 1, - redirect_uri = redirectUri, - code_verifier = secret, - code = authCode - })); - - if (tokenResponse == default) { + var tokenResponse = await Config.Client.PostAsync( + ApiHost.MakeRelativeUri("/api/auth/oauth/token"), + JsonContent.Create( + new + { + grant_type = "authorization_code", + client_id = 1, + redirect_uri = redirectUri, + code_verifier = secret, + code = authCode + })); + + if (tokenResponse == null) + { throw new Exception("Не удалось получить ответ от сайта при запросе access token"); } - if (tokenResponse.StatusCode != HttpStatusCode.OK) { + if (tokenResponse.StatusCode != HttpStatusCode.OK) + { var errorBody = await tokenResponse.Content.ReadAsStringAsync(); var snippet = string.IsNullOrWhiteSpace(errorBody) ? string.Empty : $" Ответ сервера: {TrimForLog(errorBody)}"; + throw new Exception($"Сайт не выдал access token. Код {(int)tokenResponse.StatusCode} ({tokenResponse.StatusCode}).{snippet}"); } var token = await tokenResponse.Content.ReadFromJsonAsync(); - if (token == default || string.IsNullOrWhiteSpace(token.AccessToken)) { + if (token == null || string.IsNullOrWhiteSpace(token.AccessToken)) + { throw new Exception("Сайт не вернул access token. Проверьте корректность логина и пароля"); } @@ -168,16 +203,19 @@ public override async Task Authorize() { Config.Logger.LogInformation("Успешно авторизовались"); } - protected override string GetId(Uri url) { + protected override string GetId(Uri url) + { var id = url.GetSegment(2); return id is "book" or "read" or "manga" ? url.GetSegment(3) : id; } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var bid = url.GetQueryParameter("bid"); var details = await GetBookDetails(url); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(details), Chapters = await FillChapters(details, bid), Title = string.IsNullOrWhiteSpace(details.Data.RusName) ? details.Data.Name : details.Data.RusName, @@ -189,7 +227,8 @@ public override async Task Get(Uri url) { return book; } - private async Task GetBookDetails(Uri url) { + private async Task GetBookDetails(Uri url) + { url = ApiHost .AppendSegment("api/manga/" + GetId(url)) .AppendQueryParameter("fields[]", "background") @@ -199,73 +238,87 @@ private async Task GetBookDetails(Uri url) { .AppendQueryParameter("fields[]", "chap_count"); var response = await Config.Client.GetWithTriesAsync(url); - if (response == default) { + if (response == null) + { throw new Exception("Не удалось получить ответ от сайта при загрузке информации о книге"); } - if (response.StatusCode != HttpStatusCode.OK) { + if (response.StatusCode != HttpStatusCode.OK) + { throw new Exception($"Ошибка загрузки информации о книге. Код {(int)response.StatusCode} ({response.StatusCode})."); } return await response.Content.ReadFromJsonAsync(); } - private async Task> GetToc(RanobeLibBookDetails book, string bid) { + private async Task> GetToc(RanobeLibBookDetails book, string bid) + { var url = ApiHost.MakeRelativeUri($"/api/manga/{book.Data.SlugUrl}/chapters"); Config.Logger.LogInformation("Загружаю оглавление"); var response = await Config.Client.GetWithTriesAsync(url); - if (response == default) { + if (response == null) + { throw new Exception("Не удалось получить ответ от сайта при загрузке оглавления"); } - if (response.StatusCode != HttpStatusCode.OK) { + if (response.StatusCode != HttpStatusCode.OK) + { throw new Exception($"Ошибка загрузки оглавления. Код {(int)response.StatusCode} ({response.StatusCode})."); } var result = await response.Content.ReadFromJsonAsync().ContinueWith(t => t.Result.Chapters); - if (!string.IsNullOrWhiteSpace(bid)) { + if (!string.IsNullOrWhiteSpace(bid)) + { result = result.Where(c => c.Branches.Any(b => b.BranchId.ToString() == bid)).ToList(); } - + return SliceToc(result, c => c.Name); } - private Author GetAuthor(RanobeLibBookDetails details) { + private Author GetAuthor(RanobeLibBookDetails details) + { var author = details.Data.Authors.FirstOrDefault(); - return author == default ? new Author("Ranobelib") : new Author(author.Name, SystemUrl.MakeRelativeUri($"/ru/people/{author.SlugUrl}")); + return author == null ? new Author("Ranobelib") : new Author(author.Name, SystemUrl.MakeRelativeUri($"/ru/people/{author.SlugUrl}")); } - - private IEnumerable GetCoAuthors(RanobeLibBookDetails details) { + + private IEnumerable GetCoAuthors(RanobeLibBookDetails details) + { return details.Data.Authors .Skip(1) .Select(author => new Author(author.Name, SystemUrl.MakeRelativeUri($"/ru/people/{author.SlugUrl}"))).ToList(); } - - private Task GetCover(RanobeLibBookDetails details) { + + private Task GetCover(RanobeLibBookDetails details) + { return !string.IsNullOrWhiteSpace(details.Data.Cover.Default) ? SaveImage(details.Data.Cover.Default.AsUri()) : Task.FromResult(default(TempFile)); } - protected override HttpRequestMessage GetImageRequestMessage(Uri uri) { + protected override HttpRequestMessage GetImageRequestMessage(Uri uri) + { uri = uri.Host == BaseImageHost.Host ? uri : uri.ReplaceHost(ImagesHost.Host); - + var message = base.GetImageRequestMessage(uri); message.Headers.Add("Referer", SystemUrl.ToString()); return message; } - private async Task> FillChapters(RanobeLibBookDetails book, string bid) { + private async Task> FillChapters(RanobeLibBookDetails book, string bid) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - - foreach (var socialChapter in await GetToc(book, bid)) { + + foreach (var socialChapter in await GetToc(book, bid)) + { var title = socialChapter.Name.ReplaceNewLine(); Config.Logger.LogInformation($"Загружаю главу {title.CoverQuotes()}"); - - var chapter = new Chapter { + + var chapter = new Chapter + { Title = title }; @@ -275,16 +328,18 @@ private async Task> FillChapters(RanobeLibBookDetails book, result.Add(chapter); } - + return result; } - private async Task GetChapter(RanobeLibBookDetails book, SocialLibBookChapter chapter, string bid) { + private async Task GetChapter(RanobeLibBookDetails book, SocialLibBookChapter chapter, string bid) + { var uri = ApiHost.MakeRelativeUri($"/api/manga/{book.Data.SlugUrl}/chapter?number={chapter.Number}&volume={chapter.Volume}"); - if (!string.IsNullOrWhiteSpace(bid)) { + if (!string.IsNullOrWhiteSpace(bid)) + { uri = uri.AppendQueryParameter("branch_id", bid); } - + var chapterResponse = await Config.Client.GetFromJsonWithTriesAsync(uri, TimeSpan.FromSeconds(30)); return ResponseToHtmlDoc(chapterResponse.Data); } diff --git a/Core/Logic/Getters/LibSocial/RanobeLibGetter.cs b/src/Core/Logic/Getters/LibSocial/RanobeLibGetter.cs similarity index 63% rename from Core/Logic/Getters/LibSocial/RanobeLibGetter.cs rename to src/Core/Logic/Getters/LibSocial/RanobeLibGetter.cs index a5d5f0e1..6fa45a10 100644 --- a/Core/Logic/Getters/LibSocial/RanobeLibGetter.cs +++ b/src/Core/Logic/Getters/LibSocial/RanobeLibGetter.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; @@ -10,53 +7,85 @@ using HtmlAgilityPack; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.LibSocial; +namespace Core.Logic.Getters.LibSocial; -public class RanobeLibGetter(BookGetterConfig config) : NewLibSocialGetterBase(config) { +public class RanobeLibGetter(BookGetterConfig config) : NewLibSocialGetterBase(config) +{ protected override Uri SystemUrl => new("https://ranobelib.me/"); protected override int SiteId => 3; - - private static readonly Dictionary RecursiveTag = new() { - { "paragraph", "p" }, - { "orderedList", "ol" }, - { "listItem", "li" }, - { "blockquote", "blockquote" }, + + private static readonly Dictionary RecursiveTag = new() + { + { + "paragraph", "p" + }, + { + "orderedList", "ol" + }, + { + "listItem", "li" + }, + { + "blockquote", "blockquote" + }, }; - - private static readonly Dictionary InlineTag = new() { - { "horizontalRule", "
" }, - { "hardBreak", "
" }, + + private static readonly Dictionary InlineTag = new() + { + { + "horizontalRule", "
" + }, + { + "hardBreak", "
" + }, }; - - private static readonly Dictionary MarkTag = new() { - { "italic", "i" }, - { "bold", "b" }, - { "underline", "u" }, + + private static readonly Dictionary MarkTag = new() + { + { + "italic", "i" + }, + { + "bold", "b" + }, + { + "underline", "u" + }, }; - private StringBuilder AsHtml(SocialLibBookChapter chapterResponse, IEnumerable contents) { + private StringBuilder AsHtml(SocialLibBookChapter chapterResponse, IEnumerable contents) + { var sb = new StringBuilder(); - foreach (var content in contents) { - if (RecursiveTag.TryGetValue(content.Type, out var tag)) { - sb.Append(AsHtml(chapterResponse, content.Content).ToString().CoverTag(tag)); + foreach (var content in contents) + { + if (RecursiveTag.TryGetValue(content.Type, out var tag)) + { + sb.Append(AsHtml(chapterResponse, content.Content).ToString().CoverTag(tag)); continue; } - - if (InlineTag.TryGetValue(content.Type, out tag)) { + + if (InlineTag.TryGetValue(content.Type, out tag)) + { sb.Append(tag); continue; } - switch (content.Type) { - case "text": { + switch (content.Type) + { + case "text": + { var text = content.Text.HtmlEncode(); - foreach (var mark in content.Marks) { - if (MarkTag.TryGetValue(mark.Type, out tag)) { + foreach (var mark in content.Marks) + { + if (MarkTag.TryGetValue(mark.Type, out tag)) + { text = text.CoverTag(tag); - } else { + } + else + { Config.Logger.LogInformation($"Неизвестый тип форматирования {mark.Type}"); } } @@ -64,22 +93,27 @@ private StringBuilder AsHtml(SocialLibBookChapter chapterResponse, IEnumerable[]>()) { - if (!image.TryGetValue("image", out var imageId)) { + case "image": + { + if (content.Attrs.TryGetValue("images", out var images)) + { + foreach (var image in images.Deserialize[]>()) + { + if (!image.TryGetValue("image", out var imageId)) + { continue; } - + var attachment = chapterResponse.Attachments.FirstOrDefault(a => a.Name == imageId); - if (attachment == default) { + if (attachment == null) + { continue; } - + sb.Append($""); } } - + continue; } default: @@ -90,9 +124,11 @@ private StringBuilder AsHtml(SocialLibBookChapter chapterResponse, IEnumerable e.GetValue().AsHtmlDoc(), JsonObject o => AsHtml(chapterResponse, o.Deserialize().Content).AsHtmlDoc(), _ => throw new Exception("Неизвестный тип") diff --git a/Core/Logic/Getters/LibSocial/SlashLibGetter.cs b/src/Core/Logic/Getters/LibSocial/SlashLibGetter.cs similarity index 65% rename from Core/Logic/Getters/LibSocial/SlashLibGetter.cs rename to src/Core/Logic/Getters/LibSocial/SlashLibGetter.cs index 93c901a4..8a076762 100644 --- a/Core/Logic/Getters/LibSocial/SlashLibGetter.cs +++ b/src/Core/Logic/Getters/LibSocial/SlashLibGetter.cs @@ -1,11 +1,11 @@ -using System; using Core.Configs; -namespace Core.Logic.Getters.LibSocial; +namespace Core.Logic.Getters.LibSocial; -public class SlashLibGetter : MangaLibGetter { +public class SlashLibGetter : MangaLibGetter +{ public SlashLibGetter(BookGetterConfig config) : base(config) { } - + protected override Uri SystemUrl => new("https://v2.slashlib.me/"); protected override int SiteId => 2; diff --git a/Core/Logic/Getters/LibboxGetter.cs b/src/Core/Logic/Getters/LibboxGetter.cs similarity index 75% rename from Core/Logic/Getters/LibboxGetter.cs rename to src/Core/Logic/Getters/LibboxGetter.cs index 63edb943..33b2ca23 100644 --- a/Core/Logic/Getters/LibboxGetter.cs +++ b/src/Core/Logic/Getters/LibboxGetter.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -11,19 +7,22 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class LibboxGetter(BookGetterConfig config) : GetterBase(config) { +public class LibboxGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://libbox.ru/"); - - public override async Task Get(Uri url) { + + public override async Task Get(Uri url) + { var bookId = GetId(url); url = SystemUrl.MakeRelativeUri($"/book/{bookId}"); - + var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var title = doc.GetTextBySelector("h1.product__title"); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(bookId, title), Title = title, @@ -32,44 +31,52 @@ public override async Task Get(Uri url) { Seria = GetSeria(doc, url) }; - return book; + return book; } - - private static Seria GetSeria(HtmlDocument doc, Uri url) { + + private static Seria GetSeria(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("div.product__meta a[href*=/book-series/]"); - if (string.IsNullOrWhiteSpace(a.GetText())) { - return default; + if (string.IsNullOrWhiteSpace(a.GetText())) + { + return null; } - return new Seria { - Name = a.GetText(), - Url = url.MakeRelativeUri(a.Attributes["href"].Value) + return new Seria + { + Name = a.GetText(), Url = url.MakeRelativeUri(a.Attributes["href"].Value) }; } - private async Task AddChapter(ICollection chapters, Chapter chapter, StringBuilder text) { - if (chapter == null) { + private async Task AddChapter(ICollection chapters, Chapter chapter, StringBuilder text) + { + if (chapter == null) + { return; } - + var chapterDoc = text.AsHtmlDoc(); chapter.Images = await GetImages(chapterDoc, SystemUrl); chapter.Content = chapterDoc.DocumentNode.InnerHtml; chapters.Add(chapter); } - private static bool IsSingleChapter(IEnumerable nodes) { + private static bool IsSingleChapter(IEnumerable nodes) + { var firstNode = nodes.First(); return firstNode.Name != "h2" || string.IsNullOrWhiteSpace(firstNode.InnerText); } - private static Chapter CreateChapter(string title) { - return new Chapter { + private static Chapter CreateChapter(string title) + { + return new Chapter + { Title = title }; } - private async Task GetPages(string bookId) { + private async Task GetPages(string bookId) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri($"/books/{bookId}")); return doc .QuerySelectorAll("ul.pagination a.page-numbers") @@ -78,18 +85,21 @@ private async Task GetPages(string bookId) { .Max(); } - private async Task> FillChapters(string bookId, string title) { + private async Task> FillChapters(string bookId, string title) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - + Chapter chapter = null; var singleChapter = true; var text = new StringBuilder(); var pages = await GetPages(bookId); - - for (var i = 1; i <= pages; i++) { + + for (var i = 1; i <= pages; i++) + { Config.Logger.LogInformation($"Получаю страницу {i}/{pages}"); var page = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri($"/books/{bookId}?page_book={i}")); @@ -98,16 +108,24 @@ private async Task> FillChapters(string bookId, string titl nodes = nodes.Count == 0 ? content.RemoveNodes("script, ins").ChildNodes : nodes; singleChapter = i == 1 ? IsSingleChapter(nodes) : singleChapter; - foreach (var node in nodes) { - if (singleChapter || node.Name != "h2") { - if (node.Name == "img" && node.Attributes["src"] != null) { + foreach (var node in nodes) + { + if (singleChapter || node.Name != "h2") + { + if (node.Name == "img" && node.Attributes["src"] != null) + { text.Append($""); - } else { - if (!string.IsNullOrWhiteSpace(node.InnerHtml)) { + } + else + { + if (!string.IsNullOrWhiteSpace(node.InnerHtml)) + { text.Append(node.InnerHtml.HtmlDecode().CoverTag("p")); } } - } else { + } + else + { await AddChapter(result, chapter, text); text.Clear(); chapter = CreateChapter(node.InnerHtml.HtmlDecode()); @@ -119,13 +137,15 @@ private async Task> FillChapters(string bookId, string titl return result; } - private static Author GetAuthor(HtmlDocument doc, Uri url) { + private static Author GetAuthor(HtmlDocument doc, Uri url) + { var author = doc.QuerySelector("div.author a"); return new Author(author.InnerText.HtmlDecode(), url.MakeRelativeUri(author.Attributes["href"]?.Value ?? string.Empty)); } - - private Task GetCover(HtmlDocument doc, Uri bookUri) { + + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("div.book-images img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/LibkingGetter.cs b/src/Core/Logic/Getters/LibkingGetter.cs similarity index 69% rename from Core/Logic/Getters/LibkingGetter.cs rename to src/Core/Logic/Getters/LibkingGetter.cs index b2facd98..373c355a 100644 --- a/Core/Logic/Getters/LibkingGetter.cs +++ b/src/Core/Logic/Getters/LibkingGetter.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -12,15 +9,18 @@ namespace Core.Logic.Getters; -public class LibkingGetter(BookGetterConfig config) : GetterBase(config) { +public class LibkingGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://libking.ru/"); - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var name = doc.GetTextBySelector("div[itemprop=name]"); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url, name), Title = name, @@ -31,103 +31,125 @@ public override async Task Get(Uri url) { return book; } - private Author GetAuthor(HtmlDocument doc) { + private Author GetAuthor(HtmlDocument doc) + { var a = doc.QuerySelector("a[itemprop=author]"); - return a == default ? new Author("LibKing") : new Author(a.GetText(), SystemUrl.MakeRelativeUri(a.Attributes["href"].Value)); + return a == null ? new Author("LibKing") : new Author(a.GetText(), SystemUrl.MakeRelativeUri(a.Attributes["href"].Value)); } - private Task GetCover(HtmlDocument doc, Uri bookUri) { + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("div.mov-img img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - private async Task AddChapter(ICollection chapters, Chapter chapter, StringBuilder text) { + private async Task AddChapter(ICollection chapters, Chapter chapter, StringBuilder text) + { var chapterDoc = text.AsHtmlDoc(); chapter.Images = await GetImages(chapterDoc, SystemUrl); chapter.Content = chapterDoc.DocumentNode.InnerHtml; chapters.Add(chapter); } - - private static Chapter CreateChapter(string title) { - return new Chapter { + + private static Chapter CreateChapter(string title) + { + return new Chapter + { Title = title }; } - private Uri GetFirstPage(HtmlDocument doc, Uri url) { + private Uri GetFirstPage(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("div.pages a.pagenav"); - if (a.GetText() != "1") { + if (a.GetText() != "1") + { return url; } return url.MakeRelativeUri(a.Attributes["href"].Value); } - private Uri GetNextPage(HtmlDocument doc, Uri url) { + private Uri GetNextPage(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("a.load_text"); - if (a == default) { - return default; + if (a == null) + { + return null; } return url.MakeRelativeUri(a.Attributes["href"].Value); } - private bool IsTitle(HtmlNode node) { + private bool IsTitle(HtmlNode node) + { return node.Name == "div" && node.Attributes["class"]?.Value.Contains("title") == true; } - private HtmlNode GetTitleNode(HtmlDocument doc) { - foreach (var node in doc.DocumentNode.ChildNodes) { - if (node.Name == "#text" && string.IsNullOrWhiteSpace(node.GetText())) { + private HtmlNode GetTitleNode(HtmlDocument doc) + { + foreach (var node in doc.DocumentNode.ChildNodes) + { + if (node.Name == "#text" && string.IsNullOrWhiteSpace(node.GetText())) + { continue; } - if (IsTitle(node)) { + if (IsTitle(node)) + { return node; } - return default; + return null; } - return default; + return null; } - - private async Task> FillChapters(HtmlDocument doc, Uri baseUrl, string name) { + + private async Task> FillChapters(HtmlDocument doc, Uri baseUrl, string name) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - + var fullText = new StringBuilder(); var i = 1; - for (var url = GetFirstPage(doc, baseUrl); url != default;) { + for (var url = GetFirstPage(doc, baseUrl); url != null;) + { Config.Logger.LogInformation($"Получаю страницу {i}"); doc = await Config.Client.GetHtmlDocWithTriesAsync(url); url = GetNextPage(doc, baseUrl); fullText.Append(doc.QuerySelector("#book").RemoveNodes(".navigation, .cpab, .cpab1, .load_text, #twentypersent").InnerHtml); i++; } - + var book = fullText.AsHtmlDoc(); var node = GetTitleNode(book); - if (node != default) { - while (node != default) { + if (node != null) + { + while (node != null) + { var title = node.GetText(); var chapterText = new StringBuilder(); - do { + do + { node = node.NextSibling; chapterText.Append(node.OuterHtml); - } while (node.NextSibling != default && !IsTitle(node.NextSibling)); + } while (node.NextSibling != null && !IsTitle(node.NextSibling)); node = node.NextSibling; await AddChapter(result, CreateChapter(title), chapterText); } - } else { + } + else + { await AddChapter(result, CreateChapter(name), fullText); } - + return result; } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/LibrebookGetter.cs b/src/Core/Logic/Getters/LibrebookGetter.cs similarity index 71% rename from Core/Logic/Getters/LibrebookGetter.cs rename to src/Core/Logic/Getters/LibrebookGetter.cs index 9212b100..de2b648d 100644 --- a/Core/Logic/Getters/LibrebookGetter.cs +++ b/src/Core/Logic/Getters/LibrebookGetter.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; using System.Text.Json; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -16,107 +11,140 @@ namespace Core.Logic.Getters; -public class LibrebookGetter(BookGetterConfig config) : GetterBase(config) { +public class LibrebookGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://librebook.me/"); - public override bool IsSameUrl(Uri url) { + public override bool IsSameUrl(Uri url) + { return url.IsSameSubDomain(SystemUrl); } - protected override string GetId(Uri url) { + protected override string GetId(Uri url) + { return url.GetSegment(1); } - public override async Task Init() { + public override async Task Init() + { await base.Init(); Config.Client.DefaultRequestHeaders.Add("Referer", SystemUrl.ToString()); } - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } var loginPage = "https://1.grouple.co/internal/auth/login?siteId=6&targetUri=%2Flogin%2FcontinueSso%3FsiteId%3D6%26targetUri%3D%252F".AsUri(); var doc = await Config.Client.GetHtmlDocWithTriesAsync(loginPage); - + var formUrl = loginPage.MakeRelativeUri(doc.QuerySelector("form")?.Attributes["action"]?.Value); doc = await Config.Client.PostHtmlDocWithTriesAsync(formUrl, GenerateAuthData()); var noty = Regex.Match(doc.ParsedText, @"showNoty\((.*?)\)"); var result = JsonSerializer.Deserialize(noty.Groups[1].Value); - if (result.Type == "success") { + if (result.Type == "success") + { Config.Logger.LogInformation("Успешно авторизовались"); - } else { + } + else + { throw new Exception($"Не удалось авторизоваться. {result.Text}"); } } - private FormUrlEncodedContent GenerateAuthData() { - var payload = new Dictionary { - { "targetUri", "/login/continueSso?siteId=6&targetUri=%2F" }, - { "username", Config.Options.Login }, - { "password", Config.Options.Password }, - { "remember_me", "true" }, - { "_remember_me_yes", string.Empty }, - { "remember_me_yes", "on" }, + private FormUrlEncodedContent GenerateAuthData() + { + var payload = new Dictionary + { + { + "targetUri", "/login/continueSso?siteId=6&targetUri=%2F" + }, + { + "username", Config.Options.Login + }, + { + "password", Config.Options.Password + }, + { + "remember_me", "true" + }, + { + "_remember_me_yes", string.Empty + }, + { + "remember_me_yes", "on" + }, }; return new FormUrlEncodedContent(payload); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri(GetId(url)).ReplaceHost(url.Host); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var chapterLink = doc.QuerySelector("a.chapter-link"); - if (chapterLink == default) { + if (chapterLink == null) + { throw new Exception("Книга недоступна для чтения. Возможно, нужна авторизация."); } - + var startUrl = url.MakeRelativeUri(chapterLink.Attributes["href"].Value); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(startUrl), Title = doc.GetTextBySelector("h1.names > span.name"), Author = GetAuthor(doc, url), Annotation = doc.QuerySelector("div.manga-description > div")?.InnerHtml }; - + return book; } - private Author GetAuthor(HtmlDocument doc, Uri bookUrl) { + private Author GetAuthor(HtmlDocument doc, Uri bookUrl) + { var a = doc.QuerySelector("span.elem_author a"); - return a == default ? new Author("Librebook") : new Author(a.GetText(), bookUrl.MakeRelativeUri(a.Attributes["href"].Value)); + return a == null ? new Author("Librebook") : new Author(a.GetText(), bookUrl.MakeRelativeUri(a.Attributes["href"].Value)); } - private async Task> FillChapters(Uri startUrl) { + private async Task> FillChapters(Uri startUrl) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - - foreach (var urlChapter in await GetToc(startUrl)) { + + foreach (var urlChapter in await GetToc(startUrl)) + { Config.Logger.LogInformation($"Загружаю главу {urlChapter.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = urlChapter.Title }; var chapterDoc = await GetChapter(urlChapter); - if (chapterDoc != default) { + if (chapterDoc != null) + { chapter.Images = await GetImages(chapterDoc, SystemUrl); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } - + result.Add(chapter); } return result; } - private async Task> GetToc(Uri startUrl) { + private async Task> GetToc(Uri startUrl) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(startUrl); var result = doc .QuerySelectorAll("#chapters-list a.chapter-link") @@ -126,13 +154,15 @@ private async Task> GetToc(Uri startUrl) { return SliceToc(result, c => c.Title); } - private async Task GetChapter(UrlChapter urlChapter) { + private async Task GetChapter(UrlChapter urlChapter) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(urlChapter.Url); return doc.QuerySelector("div.b-chapter").InnerHtml.AsHtmlDoc(); } - private Task GetCover(HtmlDocument doc, Uri bookUri) { + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("img.fotorama__img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/LibstGetter.cs b/src/Core/Logic/Getters/LibstGetter.cs similarity index 67% rename from Core/Logic/Getters/LibstGetter.cs rename to src/Core/Logic/Getters/LibstGetter.cs index 61ad4103..a80af18b 100644 --- a/Core/Logic/Getters/LibstGetter.cs +++ b/src/Core/Logic/Getters/LibstGetter.cs @@ -1,11 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Net.Http; using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -14,30 +9,33 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class LibstGetter(BookGetterConfig config) : GetterBase(config) { +public class LibstGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://libst.ru/"); - protected override string GetId(Uri url) => url.GetQueryParameter("BookID") ?? base.GetId(url); + protected override string GetId(Uri url) + => url.GetQueryParameter("BookID") ?? base.GetId(url); - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var bookId = GetId(url); url = SystemUrl.MakeRelativeUri($"/Detail/BookView/{bookId}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - - var book = new Book(url) { - Cover = await GetCover(doc, url), - Chapters = await FillChapters(doc), - Title = doc.GetTextBySelector("h1"), - Author = GetAuthor(doc, url), + + var book = new Book(url) + { + Cover = await GetCover(doc, url), Chapters = await FillChapters(doc), Title = doc.GetTextBySelector("h1"), Author = GetAuthor(doc, url), }; - + return book; } - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } @@ -46,38 +44,46 @@ public override async Task Authorize() { doc = await Config.Client.PostHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri("/Account/Login?ReturnUrl=/"), GenerateAuthData(token)); var error = doc.GetTextBySelector("div.validation-summary-errors"); - if (string.IsNullOrEmpty(error)) { + if (string.IsNullOrEmpty(error)) + { Config.Logger.LogInformation("Авторизация прошла успешно"); - } else { + } + else + { throw new Exception($"Не удалось авторизоваться. {error}"); } } - - private FormUrlEncodedContent GenerateAuthData(string token) { - var data = new Dictionary { - ["__RequestVerificationToken"] = token, - ["username"] = Config.Options.Login, - ["password"] = Config.Options.Password + + private FormUrlEncodedContent GenerateAuthData(string token) + { + var data = new Dictionary + { + ["__RequestVerificationToken"] = token, ["username"] = Config.Options.Login, ["password"] = Config.Options.Password }; return new FormUrlEncodedContent(data); } - private async Task> FillChapters(HtmlDocument doc) { + private async Task> FillChapters(HtmlDocument doc) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var bookChapter in GetToc(doc)) { + foreach (var bookChapter in GetToc(doc)) + { Config.Logger.LogInformation($"Загружаю главу {bookChapter.Title.CoverQuotes()}"); - - var chapter = new Chapter { + + var chapter = new Chapter + { Title = bookChapter.Title }; var chapterDoc = await GetChapter(bookChapter); - if (chapterDoc != default) { + if (chapterDoc != null) + { chapter.Content = chapterDoc.DocumentNode.InnerHtml; } @@ -87,16 +93,20 @@ private async Task> FillChapters(HtmlDocument doc) { return result; } - private async Task GetChapter(IdChapter bookChapter) { - if (string.IsNullOrWhiteSpace(bookChapter.Id)) { - return default; + private async Task GetChapter(IdChapter bookChapter) + { + if (string.IsNullOrWhiteSpace(bookChapter.Id)) + { + return null; } - + var sb = new StringBuilder(); - - for (var i = 1;; i++) { + + for (var i = 1;; i++) + { var response = await Config.Client.GetAsync(SystemUrl.MakeRelativeUri($"/Detail/GetChapterTextForCanvas?ChapID={bookChapter.Id}&k=0&page={i}")); - if (response.StatusCode != HttpStatusCode.OK) { + if (response.StatusCode != HttpStatusCode.OK) + { break; } @@ -107,14 +117,19 @@ private async Task GetChapter(IdChapter bookChapter) { return sb.AsHtmlDoc(); } - private IEnumerable GetToc(HtmlDocument doc) { + private IEnumerable GetToc(HtmlDocument doc) + { var result = new List(); - foreach (var span in doc.QuerySelectorAll("span.mt-action-author")) { + foreach (var span in doc.QuerySelectorAll("span.mt-action-author")) + { var onclick = span.Attributes["onclick"]?.Value; - if (string.IsNullOrWhiteSpace(onclick)) { + if (string.IsNullOrWhiteSpace(onclick)) + { result.Add(new IdChapter(string.Empty, span.GetText())); - } else { + } + else + { var id = Regex.Match(onclick, @"ChapID=(?\d+)").Groups["id"].Value; result.Add(new IdChapter(id, span.GetText())); } @@ -123,13 +138,15 @@ private IEnumerable GetToc(HtmlDocument doc) { return SliceToc(result, c => c.Title); } - private Task GetCover(HtmlDocument doc, Uri uri) { + private Task GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("img.coverBook")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - - private static Author GetAuthor(HtmlDocument doc, Uri uri) { + + private static Author GetAuthor(HtmlDocument doc, Uri uri) + { var a = doc.QuerySelector("h2.author_name a"); return new Author(a.GetText(), uri.MakeRelativeUri(a.Attributes["href"].Value)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/LitLifeGetter.cs b/src/Core/Logic/Getters/LitLifeGetter.cs similarity index 75% rename from Core/Logic/Getters/LitLifeGetter.cs rename to src/Core/Logic/Getters/LitLifeGetter.cs index 28e9b71c..2d26abf2 100644 --- a/Core/Logic/Getters/LitLifeGetter.cs +++ b/src/Core/Logic/Getters/LitLifeGetter.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -14,52 +9,72 @@ namespace Core.Logic.Getters; -public class LitLifeGetter(BookGetterConfig config) : GetterBase(config) { +public class LitLifeGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://litlife.club/"); - protected override string GetId(Uri url) { + protected override string GetId(Uri url) + { return url.GetSegment(2); } - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } var doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl); var token = doc.QuerySelector("#login_form input[name=_token]").Attributes["value"].Value; - + doc = await Config.Client.PostHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri("/login"), GenerateAuthData(token)); var alert = doc.QuerySelector("div.alert-danger"); - if (alert == default) { + if (alert == null) + { Config.Logger.LogInformation("Успешно авторизовались"); - } else { + } + else + { throw new Exception($"Не удалось авторизоваться. {alert.GetText()}"); } } - private FormUrlEncodedContent GenerateAuthData(string token) { - var payload = new Dictionary { - { "_token", token }, - { "login", Config.Options.Login }, - { "login_password", Config.Options.Password }, - { "remember", "on" }, + private FormUrlEncodedContent GenerateAuthData(string token) + { + var payload = new Dictionary + { + { + "_token", token + }, + { + "login", Config.Options.Login + }, + { + "login_password", Config.Options.Password + }, + { + "remember", "on" + }, }; return new FormUrlEncodedContent(payload); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var id = GetId(url); url = SystemUrl.MakeRelativeUri("/books/" + id); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var noAccess = doc.QuerySelector("p.no_access_text"); - if (noAccess != default) { + if (noAccess != null) + { throw new Exception("Книна не доступна для чтения."); } - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(id), Title = doc.GetTextBySelector("div.card-header h2[itemprop=name]"), @@ -67,28 +82,32 @@ public override async Task Get(Uri url) { Seria = GetSeria(doc), Annotation = doc.QuerySelector("#annotation")?.InnerHtml }; - + return book; } - - private Seria GetSeria(HtmlDocument doc) { + + private Seria GetSeria(HtmlDocument doc) + { var a = doc.QuerySelector("div.card-body a.sequence"); - if (a != default) { - return new Seria { - Name = a.GetText(), - Url = SystemUrl.MakeRelativeUri(a.Attributes["href"].Value) + if (a != null) + { + return new Seria + { + Name = a.GetText(), Url = SystemUrl.MakeRelativeUri(a.Attributes["href"].Value) }; } - return default; + return null; } - private Author GetAuthor(HtmlDocument doc) { + private Author GetAuthor(HtmlDocument doc) + { var a = doc.QuerySelector("div.card-body h3[itemprop=author] a.author"); return new Author(a.GetText(), SystemUrl.MakeRelativeUri(a.Attributes["href"].Value)); } - - private async Task> GetToc(string id) { + + private async Task> GetToc(string id) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri($"/books/{id}/sections/list_go_to")); var result = doc.QuerySelectorAll("ul a") @@ -98,20 +117,25 @@ private async Task> GetToc(string id) { return SliceToc(result, c => c.Title); } - private async Task> FillChapters(string id) { + private async Task> FillChapters(string id) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - - foreach (var urlChapter in await GetToc(id)) { + + foreach (var urlChapter in await GetToc(id)) + { Config.Logger.LogInformation($"Загружаю главу {urlChapter.Title.CoverQuotes()}"); - - var chapter = new Chapter { + + var chapter = new Chapter + { Title = urlChapter.Title }; - if (urlChapter.Url != default) { + if (urlChapter.Url != null) + { var chapterDoc = await GetChapter(urlChapter); chapter.Images = await GetImages(chapterDoc, urlChapter.Url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; @@ -123,9 +147,10 @@ private async Task> FillChapters(string id) { return result; } - private async Task GetChapter(UrlChapter urlChapter) { + private async Task GetChapter(UrlChapter urlChapter) + { var result = new StringBuilder(); - + var doc = await Config.Client.GetHtmlDocWithTriesAsync(urlChapter.Url); result.Append(doc.QuerySelector("div.book_text").RemoveNodes("#read_online_ad, h4").InnerHtml); @@ -133,19 +158,22 @@ private async Task GetChapter(UrlChapter urlChapter) { .Where(a => int.TryParse(a.GetText(), out _)) .Select(a => int.Parse(a.GetText())) .ToList(); - - if (pages.Count > 0) { - for (var i = 2; i <= pages.Max(); i++) { + + if (pages.Count > 0) + { + for (var i = 2; i <= pages.Max(); i++) + { doc = await Config.Client.GetHtmlDocWithTriesAsync(urlChapter.Url.AppendQueryParameter("page", i)); result.Append(doc.QuerySelector("div.book_text").RemoveNodes("#read_online_ad, h4").InnerHtml); } } - + return result.AsHtmlDoc(); } - private Task GetCover(HtmlDocument doc, Uri bookUri) { + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("div.cover img")?.Attributes["data-src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/LitgorodGetter.cs b/src/Core/Logic/Getters/LitgorodGetter.cs similarity index 76% rename from Core/Logic/Getters/LitgorodGetter.cs rename to src/Core/Logic/Getters/LitgorodGetter.cs index dc81d66e..56e7711a 100644 --- a/Core/Logic/Getters/LitgorodGetter.cs +++ b/src/Core/Logic/Getters/LitgorodGetter.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Json; using System.Text; -using System.Threading.Tasks; using System.Web; using Core.Configs; using Core.Extensions; @@ -14,17 +10,20 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class LitgorodGetter(BookGetterConfig config) : GetterBase(config) { +public class LitgorodGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://litgorod.ru/"); - - public override async Task Get(Uri url) { + + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri($"/books/view/{GetId(url)}"); Config.Client.DefaultRequestHeaders.Add("Referer", url.ToString()); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("div.b-book_item__name h1"), @@ -33,132 +32,155 @@ public override async Task Get(Uri url) { Annotation = doc.QuerySelector("div[data-tab-item=1] > p")?.InnerHtml, Seria = GetSeria(doc, url) }; - + return book; } - public override async Task Init() { + public override async Task Init() + { var response = await Config.Client.GetWithTriesAsync(SystemUrl); var doc = await response.Content.ReadAsStringAsync().ContinueWith(t => t.Result.AsHtmlDoc()); - + var csrf = doc.QuerySelector("[name=csrf-token]")?.Attributes["content"]?.Value; - if (string.IsNullOrWhiteSpace(csrf)) { + if (string.IsNullOrWhiteSpace(csrf)) + { throw new ArgumentException("Не удалось получить csrf-token", nameof(csrf)); } - + var cookies = response.Headers.SingleOrDefault(header => header.Key == "Set-Cookie").Value; var xsrfCookie = cookies.FirstOrDefault(c => c.StartsWith("XSRF-TOKEN=")); - if (xsrfCookie == null) { + if (xsrfCookie == null) + { throw new ArgumentException("Не удалось получить XSRF-TOKEN", nameof(xsrfCookie)); } - + var xsrf = HttpUtility.UrlDecode(xsrfCookie.Split(";")[0].Replace("XSRF-TOKEN=", "")); - + Config.Client.DefaultRequestHeaders.Add("Accept", "application/json, text/plain, */*"); Config.Client.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest"); Config.Client.DefaultRequestHeaders.Add("X-CSRF-TOKEN", csrf); Config.Client.DefaultRequestHeaders.Add("X-XSRF-TOKEN", xsrf); } - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } - var payload = new { - email = Config.Options.Login, - password = Config.Options.Password + var payload = new + { + email = Config.Options.Login, password = Config.Options.Password }; var response = await Config.Client.PostAsJsonAsync(SystemUrl.MakeRelativeUri("login"), payload); var data = await response.Content.ReadFromJsonAsync(); - if (string.IsNullOrWhiteSpace(data?.Message)) { + if (string.IsNullOrWhiteSpace(data?.Message)) + { Config.Logger.LogInformation("Успешно авторизовались"); - } else { + } + else + { throw new Exception($"Не удалось авторизоваться. {data.Message}"); } } - private static Author GetAuthor(HtmlDocument doc, Uri url) { + private static Author GetAuthor(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("div.b-book_item__author a"); return new Author(a.GetText(), url.MakeRelativeUri(a.Attributes["href"].Value)); } - - private static IEnumerable GetCoAuthors(HtmlDocument doc, Uri url) { + + private static IEnumerable GetCoAuthors(HtmlDocument doc, Uri url) + { return doc.QuerySelectorAll("div.b-book_item__author a").Skip(1).Select(a => new Author(a.GetText(), url.MakeRelativeUri(a.Attributes["href"].Value))).ToList(); } - private static Seria GetSeria(HtmlDocument doc, Uri url) { + private static Seria GetSeria(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("div.b-book_item__cycle a"); - if (a != default) { - return new Seria { - Name = a.GetText(), - Url = url.MakeRelativeUri(a.Attributes["href"].Value) + if (a != null) + { + return new Seria + { + Name = a.GetText(), Url = url.MakeRelativeUri(a.Attributes["href"].Value) }; } - return default; + return null; } - - private async Task> FillChapters(HtmlDocument doc, Uri uri) { + + private async Task> FillChapters(HtmlDocument doc, Uri uri) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var bookChapter in GetToc(doc, uri)) { - var chapter = new Chapter { + foreach (var bookChapter in GetToc(doc, uri)) + { + var chapter = new Chapter + { Title = bookChapter.Title }; Config.Logger.LogInformation($"Загружаю главу {bookChapter.Title.CoverQuotes()}"); var chapterDoc = await GetChapter(bookChapter.Url); - if (chapterDoc != default) { + if (chapterDoc != null) + { chapter.Images = await GetImages(chapterDoc, uri); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } - + result.Add(chapter); } return result; } - - private async Task GetChapter(Uri url) { + + private async Task GetChapter(Uri url) + { var response = await Config.Client.GetWithTriesAsync(url); - if (response == default) { - return default; + if (response == null) + { + return null; } var doc = await response.Content.ReadAsStreamAsync().ContinueWith(t => t.Result.AsHtmlDoc()); var content = doc.QuerySelector("book-reader"); - if (content == default) { - return default; + if (content == null) + { + return null; } var sb = new StringBuilder(); var chapterContent = doc.QuerySelector("book-reader").Attributes[":current_chapter"].Value; - foreach (var paragraph in HttpUtility.HtmlDecode(chapterContent).Deserialize().Paragraphs) { + foreach (var paragraph in HttpUtility.HtmlDecode(chapterContent).Deserialize().Paragraphs) + { sb.Append(paragraph.HtmlDecode()); } return sb.AsHtmlDoc(); } - private IEnumerable GetToc(HtmlDocument doc, Uri url) { + private IEnumerable GetToc(HtmlDocument doc, Uri url) + { var result = doc .QuerySelectorAll("div.b-tab__content ul.list-unstyled a") .Select(a => new UrlChapter(url.MakeRelativeUri(a.Attributes["href"].Value), string.IsNullOrWhiteSpace(a.GetText()) ? "Без названия" : a.GetText())) .ToList(); - + return SliceToc(result, c => c.Title); } - private Task GetCover(HtmlDocument doc, Uri uri) { + private Task GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("div.b-book_cover a[href]")?.Attributes["href"]?.Value ?? doc.QuerySelector("div.b-book_cover img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/LitmarketGetter.cs b/src/Core/Logic/Getters/LitmarketGetter.cs similarity index 74% rename from Core/Logic/Getters/LitmarketGetter.cs rename to src/Core/Logic/Getters/LitmarketGetter.cs index 4590ddec..c18f707e 100644 --- a/Core/Logic/Getters/LitmarketGetter.cs +++ b/src/Core/Logic/Getters/LitmarketGetter.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Json; using System.Text; -using System.Threading.Tasks; using System.Web; using Core.Configs; using Core.Extensions; @@ -14,47 +10,53 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class LitmarketGetter(BookGetterConfig config) : GetterBase(config) { +public class LitmarketGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://litmarket.ru"); - public override async Task Init() { + public override async Task Init() + { await base.Init(); - + Config.Client.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest"); - + var response = await Config.Client.GetWithTriesAsync(SystemUrl); var doc = await response.Content.ReadAsStreamAsync().ContinueWith(t => t.Result.AsHtmlDoc()); var csrf = doc.QuerySelector("[name=csrf-token]")?.Attributes["content"]?.Value; - if (string.IsNullOrWhiteSpace(csrf)) { + if (string.IsNullOrWhiteSpace(csrf)) + { throw new ArgumentException("Не удалось получить csrf-token", nameof(csrf)); } - + var cookies = response.Headers.SingleOrDefault(header => header.Key == "Set-Cookie").Value; var xsrfCookie = cookies.FirstOrDefault(c => c.StartsWith("XSRF-TOKEN=")); - if (xsrfCookie == null) { + if (xsrfCookie == null) + { throw new ArgumentException("Не удалось получить XSRF-TOKEN", nameof(xsrfCookie)); } - + var xsrf = HttpUtility.UrlDecode(xsrfCookie.Split(";")[0].Replace("XSRF-TOKEN=", "")); - + Config.Client.DefaultRequestHeaders.Add("X-CSRF-TOKEN", csrf); Config.Client.DefaultRequestHeaders.Add("X-XSRF-TOKEN", xsrf); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var bookId = GetId(url); url = SystemUrl.MakeRelativeUri($"/books/{bookId}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var content = await GetMainData(bookId); - + var blocks = await GetBlocks(content.Book.EbookId); var title = doc.GetTextBySelector("h1.card-title").ReplaceNewLine(); - var book = new Book(url.ReplaceHost(SystemUrl.Host)) { + var book = new Book(url.ReplaceHost(SystemUrl.Host)) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(GetToc(content, title), blocks, url, content.Book.EbookId), Title = title, @@ -62,71 +64,87 @@ public override async Task Get(Uri url) { Annotation = doc.QuerySelector("div.card-description")?.InnerHtml, Seria = GetSeria(doc, url) }; - + return book; } - - private static Seria GetSeria(HtmlDocument doc, Uri url) { + + private static Seria GetSeria(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("div.card-cycle a"); - if (a == default || !a.GetText().Contains('#')) { - return default; + if (a == null || !a.GetText().Contains('#')) + { + return null; } - + var parts = a.GetText().Split('#', StringSplitOptions.RemoveEmptyEntries); - return new Seria { - Name = parts[0].HtmlDecode(), - Number = parts[1].HtmlDecode(), - Url = url.MakeRelativeUri(a.Attributes["href"].Value) + return new Seria + { + Name = parts[0].HtmlDecode(), Number = parts[1].HtmlDecode(), Url = url.MakeRelativeUri(a.Attributes["href"].Value) }; } - private Author GetAuthor(HtmlDocument doc, Uri url) { + private Author GetAuthor(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("div.card-author a"); - return a == default ? - new Author(doc.GetTextBySelector("div.card-author").Replace("Автор:", "").ReplaceNewLine()): + return a == null ? + new Author(doc.GetTextBySelector("div.card-author").Replace("Автор:", "").ReplaceNewLine()) : new Author(a.GetText().ReplaceNewLine(), url.MakeRelativeUri(a.Attributes["href"].Value).ReplaceHost(SystemUrl.Host)); } - + /// /// Авторизация в системе /// /// - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } - var payload = new { - email = Config.Options.Login, - password = Config.Options.Password + var payload = new + { + email = Config.Options.Login, password = Config.Options.Password }; - + using var post = await Config.Client.PostAsJsonAsync(SystemUrl.MakeRelativeUri("/auth/login"), payload); - try { + try + { var data = await post.Content.ReadFromJsonAsync(); - if (data is { Success: false }) { + if (data is { Success: false }) + { throw new Exception("Не удалось авторизоваться"); } - } catch { + } + catch + { throw new Exception("Не удалось авторизоваться"); } } - private Task GetCover(HtmlDocument doc, Uri uri) { + private Task GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("div.front img")?.Attributes["data-src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath.AsUri().AbsolutePath)) : Task.FromResult(default(TempFile)); } - private List GetToc(Response response, string title) { + private List GetToc(Response response, string title) + { var toc = response.Toc.Deserialize>(); - if (toc?.Count == 0) { - toc = [ - new() { + if (toc?.Count == 0) + { + toc = + [ + new() + { Index = 0, - Chunk = new Chunk { - Mods = [ - new Mod { + Chunk = new Chunk + { + Mods = + [ + new Mod + { Text = title } ] @@ -138,23 +156,29 @@ private List GetToc(Response response, string title) { return SliceToc(toc, c => c.Chunk.Mods[0].Text).ToList(); } - private async Task> FillChapters(List toc, Block[] blocks, Uri bookUri, long eBookId) { + private async Task> FillChapters(List toc, Block[] blocks, Uri bookUri, long eBookId) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - for (var i = 0; i < toc.Count; i++) { + for (var i = 0; i < toc.Count; i++) + { var chapterTitle = string.IsNullOrWhiteSpace(toc[i].Chunk.Mods[0].Text.Trim()) ? "Без названия" : toc[i].Chunk.Mods[0].Text.Trim(); Config.Logger.LogInformation($"Загружаю главу {chapterTitle.CoverQuotes()}"); var sb = new StringBuilder(); var chapter = new Chapter(); - foreach (var block in blocks.Where(b => b.Index >= toc[i].Index && (i == toc.Count -1 || b.Index < toc[i + 1].Index))) { + foreach (var block in blocks.Where(b => b.Index >= toc[i].Index && (i == toc.Count - 1 || b.Index < toc[i + 1].Index))) + { var p = new StringBuilder(); - - foreach (var mod in block.Chunk.Mods) { - switch (mod.Type) { + + foreach (var mod in block.Chunk.Mods) + { + switch (mod.Type) + { case "IMAGE": p.Append($""); break; @@ -163,9 +187,12 @@ private async Task> FillChapters(List toc, Block[] blocks, break; default: var text = $"{mod.Text ?? string.Empty}"; - if (mod.Styles?.Length > 0) { - foreach (var style in mod.Styles) { - switch (style) { + if (mod.Styles?.Length > 0) + { + foreach (var style in mod.Styles) + { + switch (style) + { case "ITALIC": text = text.CoverTag("em"); break; @@ -201,13 +228,15 @@ private async Task> FillChapters(List toc, Block[] blocks, return result; } - private async Task GetMainData(string bookId) { + private async Task GetMainData(string bookId) + { var data = await Config.Client.GetWithTriesAsync(SystemUrl.MakeRelativeUri($"/reader/data/{bookId}")); return await data.Content.ReadFromJsonAsync(); } - private async Task GetBlocks(int eBookId) { + private async Task GetBlocks(int eBookId) + { var resp = await Config.Client.GetWithTriesAsync(SystemUrl.MakeRelativeUri($"/reader/blocks/{eBookId}")); return await resp.Content.ReadFromJsonAsync(); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/LitmirGetter.cs b/src/Core/Logic/Getters/LitmirGetter.cs similarity index 73% rename from Core/Logic/Getters/LitmirGetter.cs rename to src/Core/Logic/Getters/LitmirGetter.cs index f5b3d27a..84fe6308 100644 --- a/Core/Logic/Getters/LitmirGetter.cs +++ b/src/Core/Logic/Getters/LitmirGetter.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -11,14 +7,17 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class LitmirGetter(BookGetterConfig config) : GetterBase(config) { +public class LitmirGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://litmir.me/"); - protected override string GetId(Uri url) => url.GetQueryParameter("b"); + protected override string GetId(Uri url) + => url.GetQueryParameter("b"); - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var bookId = GetId(url); url = SystemUrl.MakeRelativeUri($"/bd/?b={bookId}"); @@ -27,7 +26,8 @@ public override async Task Get(Uri url) { var name = doc.GetTextBySelector("div[itemprop=name]"); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(bookId, pages, name), Title = name, @@ -35,112 +35,141 @@ public override async Task Get(Uri url) { Annotation = doc.QuerySelector("div[itemprop=description]")?.InnerHtml, Seria = GetSeria(doc, url) }; - - return book; + + return book; } - - private static Seria GetSeria(HtmlDocument doc, Uri url) { + + private static Seria GetSeria(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("td.bd_desc2 a[href^=/books_in_series/]"); - if (a != default) { + if (a != null) + { var text = a.GetText(); - if (!string.IsNullOrWhiteSpace(text)) { - var seria = new Seria { + if (!string.IsNullOrWhiteSpace(text)) + { + var seria = new Seria + { Url = url.MakeRelativeUri(a.Attributes["href"].Value) }; - - if (text.Contains('#')) { + + if (text.Contains('#')) + { var split = text.Split("#"); - if (int.TryParse(split.Last(), out var number)) { + if (int.TryParse(split.Last(), out var number)) + { seria.Number = number.ToString(); seria.Name = split[0].Trim(); - } else { + } + else + { seria.Name = text; } - } else { + } + else + { seria.Name = text; } - + return seria; } - - } - return default; + return null; } - - private Task GetCover(HtmlDocument doc, Uri bookUri) { + + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("img[jq=BookCover]")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - private async Task AddChapter(ICollection chapters, Chapter chapter, StringBuilder text) { - if (chapter == null) { + private async Task AddChapter(ICollection chapters, Chapter chapter, StringBuilder text) + { + if (chapter == null) + { return; } - + var chapterDoc = text.AsHtmlDoc(); chapter.Images = await GetImages(chapterDoc, SystemUrl.MakeRelativeUri("/br/")); chapter.Content = chapterDoc.DocumentNode.InnerHtml; chapters.Add(chapter); } - private static bool IsChapterHeader(HtmlNode node) { + private static bool IsChapterHeader(HtmlNode node) + { return node.Name == "a" && node.Attributes["name"]?.Value?.StartsWith("section") == true; } - private static bool IsSingleChapter(IEnumerable nodes) { + private static bool IsSingleChapter(IEnumerable nodes) + { var firstNode = nodes.First(); return !IsChapterHeader(firstNode) || string.IsNullOrWhiteSpace(firstNode.InnerText); } - private static Chapter CreateChapter(string title) { - return new Chapter { + private static Chapter CreateChapter(string title) + { + return new Chapter + { Title = title }; } - private async Task GetChapter(string bookId, int page) { + private async Task GetChapter(string bookId, int page) + { using var response = await Config.Client.GetWithTriesAsync(SystemUrl.MakeRelativeUri($"/br/?b={bookId}&p={page}")); - if (response == default) { - return default; + if (response == null) + { + return null; } var doc = await response.Content.ReadAsStreamAsync().ContinueWith(t => t.Result.AsHtmlDoc()); - foreach (var node in doc.QuerySelectorAll("p")) { + foreach (var node in doc.QuerySelectorAll("p")) + { node.Attributes.RemoveAll(); } - + return doc; } - private async Task> FillChapters(string bookId, long pages, string name) { + private async Task> FillChapters(string bookId, long pages, string name) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - + Chapter chapter = null; var singleChapter = true; var text = new StringBuilder(); - - for (var i = 1; i <= pages; i++) { + + for (var i = 1; i <= pages; i++) + { Config.Logger.LogInformation($"Получаю страницу {i}/{pages}"); var page = await GetChapter(bookId, i); var nodes = page.QuerySelector("div.page_text").RemoveNodes("div[id^=adrun], script").ChildNodes; singleChapter = i == 1 ? IsSingleChapter(nodes) : singleChapter; - foreach (var node in nodes) { - if (singleChapter || !IsChapterHeader(node)) { - if (node.Name == "img" && node.Attributes["src"] != null) { + foreach (var node in nodes) + { + if (singleChapter || !IsChapterHeader(node)) + { + if (node.Name == "img" && node.Attributes["src"] != null) + { text.Append($""); - } else { - if (!string.IsNullOrWhiteSpace(node.InnerHtml)) { + } + else + { + if (!string.IsNullOrWhiteSpace(node.InnerHtml)) + { text.Append(node.InnerHtml.HtmlDecode().CoverTag("p")); } } - } else { + } + else + { await AddChapter(result, chapter, text); text.Clear(); chapter = CreateChapter(node.InnerText.HtmlDecode()); @@ -151,4 +180,4 @@ private async Task> FillChapters(string bookId, long pages, string await AddChapter(result, chapter ?? CreateChapter(name), text); return result; } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Litnet/BooknetComGetter.cs b/src/Core/Logic/Getters/Litnet/BooknetComGetter.cs similarity index 67% rename from Core/Logic/Getters/Litnet/BooknetComGetter.cs rename to src/Core/Logic/Getters/Litnet/BooknetComGetter.cs index 1f5e78bc..fa40902b 100644 --- a/Core/Logic/Getters/Litnet/BooknetComGetter.cs +++ b/src/Core/Logic/Getters/Litnet/BooknetComGetter.cs @@ -1,8 +1,8 @@ -using System; using Core.Configs; -namespace Core.Logic.Getters.Litnet; +namespace Core.Logic.Getters.Litnet; -public class BooknetComGetter(BookGetterConfig config) : LitnetGetterBase(config) { +public class BooknetComGetter(BookGetterConfig config) : LitnetGetterBase(config) +{ protected override Uri SystemUrl => new("https://booknet.com/"); -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Litnet/BooknetUaGetter.cs b/src/Core/Logic/Getters/Litnet/BooknetUaGetter.cs similarity index 68% rename from Core/Logic/Getters/Litnet/BooknetUaGetter.cs rename to src/Core/Logic/Getters/Litnet/BooknetUaGetter.cs index 761620b6..ba40fe8e 100644 --- a/Core/Logic/Getters/Litnet/BooknetUaGetter.cs +++ b/src/Core/Logic/Getters/Litnet/BooknetUaGetter.cs @@ -1,8 +1,8 @@ -using System; using Core.Configs; -namespace Core.Logic.Getters.Litnet; +namespace Core.Logic.Getters.Litnet; -public class BooknetUaGetter(BookGetterConfig config) : LitnetGetterBase(config) { +public class BooknetUaGetter(BookGetterConfig config) : LitnetGetterBase(config) +{ protected override Uri SystemUrl => new("https://booknet.ua/"); -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Litnet/LitnetGetter.cs b/src/Core/Logic/Getters/Litnet/LitnetGetter.cs similarity index 69% rename from Core/Logic/Getters/Litnet/LitnetGetter.cs rename to src/Core/Logic/Getters/Litnet/LitnetGetter.cs index 66126f79..ed762c54 100644 --- a/Core/Logic/Getters/Litnet/LitnetGetter.cs +++ b/src/Core/Logic/Getters/Litnet/LitnetGetter.cs @@ -1,8 +1,8 @@ -using System; using Core.Configs; -namespace Core.Logic.Getters.Litnet; +namespace Core.Logic.Getters.Litnet; -public class LitnetGetter(BookGetterConfig config) : LitnetGetterBase(config) { +public class LitnetGetter(BookGetterConfig config) : LitnetGetterBase(config) +{ protected override Uri SystemUrl => new("https://litnet.com/"); -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Litnet/LitnetGetterBase.cs b/src/Core/Logic/Getters/Litnet/LitnetGetterBase.cs similarity index 73% rename from Core/Logic/Getters/Litnet/LitnetGetterBase.cs rename to src/Core/Logic/Getters/Litnet/LitnetGetterBase.cs index 2eb0870f..8a14c472 100644 --- a/Core/Logic/Getters/Litnet/LitnetGetterBase.cs +++ b/src/Core/Logic/Getters/Litnet/LitnetGetterBase.cs @@ -1,13 +1,7 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Net; -using System.Net.Http; using System.Net.Http.Json; using System.Security.Cryptography; using System.Text; -using System.Threading.Tasks; using System.Web; using Core.Configs; using Core.Extensions; @@ -20,9 +14,10 @@ namespace Core.Logic.Getters.Litnet; -public abstract class LitnetGetterBase(BookGetterConfig config) : GetterBase(config) { +public abstract class LitnetGetterBase(BookGetterConfig config) : GetterBase(config) +{ private Uri ApiUrl => new($"https://api.{SystemUrl.Host}/"); - + //cloudflare :( protected virtual Uri ApiIp => ApiUrl; @@ -33,26 +28,29 @@ public abstract class LitnetGetterBase(BookGetterConfig config) : GetterBase(con private string _token { get; set; } - protected override string GetId(Uri url) => base.GetId(url).Split('-').Last().Replace("b", string.Empty); + protected override string GetId(Uri url) + => base.GetId(url).Split('-').Last().Replace("b", string.Empty); - private static byte[] Decrypt(string text) { + private static byte[] Decrypt(string text) + { using var aes = Aes.Create(); const int IV_SHIFT = 16; - - aes.Key = Encoding.UTF8.GetBytes(SECRET); + + aes.Key = Encoding.UTF8.GetBytes(SECRET); aes.IV = Encoding.UTF8.GetBytes(text)[..IV_SHIFT]; - + var decryptor = aes.CreateDecryptor(); using var ms = new MemoryStream(Convert.FromBase64String(text)); using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read); - + var output = new MemoryStream(); cs.CopyTo(output); return output.ToArray()[IV_SHIFT..]; } - private static string GetSign(string token) { + private static string GetSign(string token) + { var inputBytes = Encoding.ASCII.GetBytes(DeviceId + SECRET + (token ?? string.Empty)); var hashBytes = MD5.HashData(inputBytes); @@ -63,63 +61,78 @@ private static string GetSign(string token) { /// Авторизация в системе /// /// - public override async Task Authorize() { + public override async Task Authorize() + { var path = Config.HasCredentials ? "user/find-by-login" : "registration/registration-by-device"; - var url = ApiIp.MakeRelativeUri($"v1/{path}?login={HttpUtility.UrlEncode(Config.Options.Login?.TrimStart('+') ?? string.Empty)}&password={HttpUtility.UrlEncode(Config.Options.Password)}&app=android&device_id={DeviceId}&sign={GetSign(string.Empty)}"); + var url = ApiIp.MakeRelativeUri( + $"v1/{path}?login={HttpUtility.UrlEncode(Config.Options.Login?.TrimStart('+') ?? string.Empty)}&password={HttpUtility.UrlEncode(Config.Options.Password)}&app=android&device_id={DeviceId}&sign={GetSign(string.Empty)}"); + var data = await GetApiData(url); - if (!string.IsNullOrWhiteSpace(data?.Token)) { + if (!string.IsNullOrWhiteSpace(data?.Token)) + { Config.Logger.LogInformation("Успешно авторизовались"); _token = data.Token; - } else { + } + else + { throw new Exception($"Не удалось авторизоваться. {data?.Error}"); } } - private async Task GetBook(string token, string bookId) { + private async Task GetBook(string token, string bookId) + { var url = ApiIp.MakeRelativeUri($"/v1/book/get/{bookId}?app=android&device_id={DeviceId}&user_token={token}&sign={GetSign(token)}"); var data = await GetApiData(url); - if (!Config.HasCredentials && data!.AdultOnly) { + if (!Config.HasCredentials && data!.AdultOnly) + { throw new Exception("Произведение 18+. Необходимо добавить логин и пароль."); } - + return data; } - private Task GetBookContents(string token, string bookId) { + private Task GetBookContents(string token, string bookId) + { var url = ApiIp.MakeRelativeUri($"/v1/book/contents?bookId={bookId}&app=android&device_id={DeviceId}&user_token={token}&sign={GetSign(token)}"); return GetApiData(url); } - private async Task> GetToc(string token, IEnumerable contents) { + private async Task> GetToc(string token, IEnumerable contents) + { var chapters = string.Join("&", contents.Select(t => $"chapter_ids[]={t.Id}")); var url = ApiIp.MakeRelativeUri($"/v1/book/get-chapters-texts/?{chapters}&app=android&device_id={DeviceId}&sign={GetSign(token)}&user_token={token}"); var data = await GetApiData(url); return SliceToc(data, c => c.Text); } - private async Task GetApiData(Uri uri) { + private async Task GetApiData(Uri uri) + { var response = await Config.Client.SendAsync(GetDefaultMessage(uri, ApiUrl)); - if (response.StatusCode == HttpStatusCode.Forbidden) { + if (response.StatusCode == HttpStatusCode.Forbidden) + { throw new Exception("Получен бан. Попробуйте позже."); } - if (response.StatusCode == HttpStatusCode.NotFound) { + if (response.StatusCode == HttpStatusCode.NotFound) + { return default; } - + var data = await response.Content.ReadFromJsonAsync(); return data; } - - public override async Task Get(Uri url) { + + public override async Task Get(Uri url) + { var bookId = GetId(url); var litnetBook = await GetBook(_token, bookId); var uri = SystemUrl.MakeRelativeUri(litnetBook.Url.AsUri().AbsolutePath); - var book = new Book(uri) { + var book = new Book(uri) + { Cover = await GetCover(litnetBook), Chapters = await FillChapters(_token, litnetBook, bookId), Title = litnetBook.Title.Trim(), @@ -129,65 +142,78 @@ public override async Task Get(Uri url) { Seria = await GetSeria(uri, litnetBook), Lang = litnetBook.Lang }; - + return book; } - private async Task GetSeria(Uri url, LitnetBookResponse book) { - try { + private async Task GetSeria(Uri url, LitnetBookResponse book) + { + try + { var doc = await Config.Client.SendAsync(GetDefaultMessage(url.ReplaceHost(SiteIp.Host), SystemUrl)).ContinueWith(t => t.Result.Content.ReadAsStream().AsHtmlDoc()); var a = doc.QuerySelector("div.book-view-info-coll a[href*='sort=cycles']"); - if (a != default) { - return new Seria { - Name = a.GetText(), - Url = url.MakeRelativeUri(a.Attributes["href"].Value), - Number = book.CyclePriority is > 0 ? book.CyclePriority.Value.ToString() : string.Empty + if (a != null) + { + return new Seria + { + Name = a.GetText(), Url = url.MakeRelativeUri(a.Attributes["href"].Value), Number = book.CyclePriority is > 0 ? book.CyclePriority.Value.ToString() : string.Empty }; } - } catch (Exception ex) { + } + catch (Exception ex) + { Config.Logger.LogInformation(ex.ToString()); } - return default; + return null; } - private Author GetAuthor(LitnetBookResponse book) { + private Author GetAuthor(LitnetBookResponse book) + { return new Author((book.AuthorName ?? SystemUrl.Host).Trim(), SystemUrl.MakeRelativeUri($"/ru/{book.AuthorId}")); } - - private IEnumerable GetCoAuthors(LitnetBookResponse book) { + + private IEnumerable GetCoAuthors(LitnetBookResponse book) + { var result = new List(); - if (!string.IsNullOrWhiteSpace(book.CoAuthorName) && book.CoAuthorId.HasValue) { + if (!string.IsNullOrWhiteSpace(book.CoAuthorName) && book.CoAuthorId.HasValue) + { result.Add(new Author((book.CoAuthorName ?? SystemUrl.Host).Trim(), SystemUrl.MakeRelativeUri($"/ru/{book.CoAuthorId.Value}"))); } return result; } - private static string GetAnnotation(LitnetBookResponse book) { - return string.IsNullOrWhiteSpace(book.Annotation) ? - string.Empty : + private static string GetAnnotation(LitnetBookResponse book) + { + return string.IsNullOrWhiteSpace(book.Annotation) ? + string.Empty : string.Join("", book.Annotation.Split("\n", StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim().CoverTag("p"))); } - - private Task GetCover(LitnetBookResponse book) { + + private Task GetCover(LitnetBookResponse book) + { return !string.IsNullOrWhiteSpace(book.Cover) ? SaveImage(book.Cover.AsUri()) : Task.FromResult(default(TempFile)); } - - protected override HttpRequestMessage GetImageRequestMessage(Uri uri) { - if (uri.IsSameHost(SystemUrl) || uri.IsSameSubDomain(SystemUrl)) { + + protected override HttpRequestMessage GetImageRequestMessage(Uri uri) + { + if (uri.IsSameHost(SystemUrl) || uri.IsSameSubDomain(SystemUrl)) + { return GetDefaultMessage(SystemUrl.MakeRelativeUri(uri.AbsolutePath), uri); } return base.GetImageRequestMessage(uri); } - - private HttpRequestMessage GetDefaultMessage(Uri uri, Uri host, HttpContent content = null) { - var message = new HttpRequestMessage(content == default ? HttpMethod.Get : HttpMethod.Post, uri); + + private HttpRequestMessage GetDefaultMessage(Uri uri, Uri host, HttpContent content = null) + { + var message = new HttpRequestMessage(content == null ? HttpMethod.Get : HttpMethod.Post, uri); message.Content = content; message.Version = Config.Client.DefaultRequestVersion; - - foreach (var header in Config.Client.DefaultRequestHeaders) { + + foreach (var header in Config.Client.DefaultRequestHeaders) + { message.Headers.Add(header.Key, header.Value); } @@ -196,31 +222,38 @@ private HttpRequestMessage GetDefaultMessage(Uri uri, Uri host, HttpContent cont return message; } - private async Task> FillChapters(string token, LitnetBookResponse book, string bookId) { + private async Task> FillChapters(string token, LitnetBookResponse book, string bookId) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - + var contents = await GetBookContents(token, bookId); - if (contents == default || contents.Length == 0) { + if (contents == null || contents.Length == 0) + { return result; } - + var chapters = await GetToc(token, contents); var map = chapters.ToDictionary(t => t.Id); - - foreach (var content in contents) { - if (!map.TryGetValue(content.Id, out var litnetChapter)) { + + foreach (var content in contents) + { + if (!map.TryGetValue(content.Id, out var litnetChapter)) + { litnetChapter = new LitnetChapterResponse(); } Config.Logger.LogInformation($"Загружаю главу {content.Title.Trim().CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = (content.Title ?? book.Title).Trim() }; - - if (!string.IsNullOrWhiteSpace(litnetChapter.Text)) { + + if (!string.IsNullOrWhiteSpace(litnetChapter.Text)) + { var chapterDoc = GetChapterDoc(Encoding.UTF8.GetString(Decrypt(litnetChapter.Text))); chapter.Images = await GetImages(chapterDoc, SystemUrl); chapter.Content = chapterDoc.DocumentNode.InnerHtml; @@ -232,7 +265,8 @@ private async Task> FillChapters(string token, LitnetBookResponse return result; } - private static HtmlDocument GetChapterDoc(string text) { + private static HtmlDocument GetChapterDoc(string text) + { return text.Deserialize().Aggregate(new StringBuilder(), (sb, row) => sb.Append(row)).AsHtmlDoc(); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/LitresGetter.cs b/src/Core/Logic/Getters/LitresGetter.cs similarity index 63% rename from Core/Logic/Getters/LitresGetter.cs rename to src/Core/Logic/Getters/LitresGetter.cs index 78c3e7b9..3c735d35 100644 --- a/Core/Logic/Getters/LitresGetter.cs +++ b/src/Core/Logic/Getters/LitresGetter.cs @@ -1,15 +1,9 @@ -using System; -using System.Collections.Generic; -using System.IO; using System.IO.Compression; -using System.Linq; using System.Net; -using System.Net.Http; using System.Net.Http.Json; using System.Security.Cryptography; using System.Text; using System.Text.Json; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Misc; @@ -22,74 +16,90 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class LitresGetter(BookGetterConfig config) : GetterBase(config) { +public class LitresGetter(BookGetterConfig config) : GetterBase(config) +{ private const string SECRET_KEY = "AsAAfdV000-1kksn6591x:[}A{}<> new("https://www.litres.ru"); - - private static Uri GetShortUri(LitresArt art) => new($"https://catalit.litres.ru/pub/t/{art.Id}.fb3"); - private static long GetCurrentMilli() { - var jan1970 = new DateTime(1970, 1, 1, 0, 0, 0,DateTimeKind.Utc); + private static Uri GetShortUri(LitresArt art) + => new($"https://catalit.litres.ru/pub/t/{art.Id}.fb3"); + + private static long GetCurrentMilli() + { + var jan1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); var javaSpan = DateTime.UtcNow - jan1970; return (long)javaSpan.TotalMilliseconds / 1000; } - private Uri GetFullUri(LitresArt art, string path, LitresFile file, string host = null) { + private Uri GetFullUri(LitresArt art, string path, LitresFile file, string host = null) + { var ts = GetCurrentMilli(); var inputBytes = Encoding.ASCII.GetBytes($"{ts}:{art.Id}:{SECRET_KEY}"); var hashBytes = MD5.HashData(inputBytes); - + // Текстовая книга - if (art.ArtType != LitresArtTypeEnum.Audio) { - var result = new Uri($"https://{host ?? "catalit.litres.ru"}/pages/{path}?art={art.Id}&sid={_authData.Sid}&uilang=ru&libapp={APP}×tamp={ts}&md5={Convert.ToHexString(hashBytes).ToLower()}"); - if (file == default) { + if (art.ArtType != LitresArtTypeEnum.Audio) + { + var result = new Uri( + $"https://{host ?? "catalit.litres.ru"}/pages/{path}?art={art.Id}&sid={_authData.Sid}&uilang=ru&libapp={APP}×tamp={ts}&md5={Convert.ToHexString(hashBytes).ToLower()}"); + + if (file == null) + { return result.AppendQueryParameter("type", "fb3"); } - + result = result.AppendQueryParameter("file", file.Id); - if (!string.IsNullOrWhiteSpace(file.Extension)) { + if (!string.IsNullOrWhiteSpace(file.Extension)) + { result = result.AppendQueryParameter("type", file.Extension); } return result; } - + // Аудиокнига - return new Uri($"https://{host ?? "ios.litres.ru"}/pages/{path}/{art.Id}/{file.Id}.mp3?sid={_authData.Sid}&uilang=ru&libapp={APP}×tamp={ts}&md5={Convert.ToHexString(hashBytes).ToLower()}"); + return new Uri( + $"https://{host ?? "ios.litres.ru"}/pages/{path}/{art.Id}/{file.Id}.mp3?sid={_authData.Sid}&uilang=ru&libapp={APP}×tamp={ts}&md5={Convert.ToHexString(hashBytes).ToLower()}"); } - private LitresAuthResponseData _authData = new(){ + private LitresAuthResponseData _authData = new() + { Sid = "6ufp4b2wbx1acc4k1f0wdo571762c0fn" }; private LitresMe _me; - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { Config.Client.DefaultRequestHeaders.Add("Session-Id", _authData.Sid); return; } - + // Литрес очень не любит много авторизовывать // поэтому пришлось добавить кеширование токенов const string directory = "LitresCache"; - if (!Directory.Exists(directory)) { + if (!Directory.Exists(directory)) + { Directory.CreateDirectory(directory); } var saveCreds = $"{directory}/{Config.Options.Login.RemoveInvalidChars()}"; - if (File.Exists(saveCreds)) { + if (File.Exists(saveCreds)) + { _authData = await File.ReadAllTextAsync(saveCreds).ContinueWith(t => t.Result.Deserialize()); Config.Client.DefaultRequestHeaders.Add("Session-Id", _authData.Sid); - + var checkResponse = await Config.Client.GetFromJsonAsync>("https://api.litres.ru/foundation/api/users/me/detailed"); - if (checkResponse?.Payload?.Data != default) { + if (checkResponse?.Payload?.Data != null) + { _me = checkResponse.Payload.Data; return; } @@ -97,16 +107,18 @@ public override async Task Authorize() { var payload = LitresPayload.Create(DateTime.Now, string.Empty, SECRET_KEY, APP); payload.Requests.Add(new LitresAuthRequest(Config.Options.Login, Config.Options.Password)); - + _authData = await GetResponse(payload); - if (!_authData.Success) { + if (!_authData.Success) + { throw new Exception($"Не удалось авторизоваться. {_authData.ErrorMessage}"); } - + Config.Client.DefaultRequestHeaders.Add("Session-Id", _authData.Sid); var meResponse = await Config.Client.GetFromJsonAsync>("https://api.litres.ru/foundation/api/users/me/detailed"); - if (meResponse?.Payload?.Data == default) { + if (meResponse?.Payload?.Data == null) + { return; } @@ -114,34 +126,41 @@ public override async Task Authorize() { await File.WriteAllTextAsync(saveCreds, JsonSerializer.Serialize(_authData)); } - private async Task GetResponse(LitresPayload payload) { + private async Task GetResponse(LitresPayload payload) + { var resp = await Config.Client.PostWithTriesAsync("https://catalit.litres.ru/catalitv2".AsUri(), CreatePayload(payload)); return await resp.Content.ReadAsStringAsync().ContinueWith(t => t.Result.Deserialize>().Data); } - - private async Task GetResponse(Uri url) { + + private async Task GetResponse(Uri url) + { var resp = await Config.Client.GetWithTriesAsync(url); - if (resp == default) { + if (resp == null) + { return default; } - + return await resp.Content.ReadAsStringAsync().ContinueWith(t => t.Result.Deserialize>().Payload.Data); } - private static FormUrlEncodedContent CreatePayload(LitresPayload payload) { - var d = new Dictionary { + private static FormUrlEncodedContent CreatePayload(LitresPayload payload) + { + var d = new Dictionary + { ["jdata"] = JsonSerializer.Serialize(payload) }; return new FormUrlEncodedContent(d); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var bookId = GetBookId(url); var art = await GetResponse($"https://api.litres.ru/foundation/api/arts/{bookId}".AsUri()); - - var book = new Book(SystemUrl.MakeRelativeUri(bookId)) { + + var book = new Book(SystemUrl.MakeRelativeUri(bookId)) + { Cover = await GetCover(art.Cover), Title = art.Title, Author = await GetAuthor(art), @@ -149,22 +168,29 @@ public override async Task Get(Uri url) { Annotation = art.Annotation, Seria = GetSeria(art) }; - + await FillAdditional(book, art); - foreach (var linked in art.LinkedArts ?? []) { + foreach (var linked in art.LinkedArts ?? []) + { await FillAdditional(book, linked); } - + var fb3File = book.AdditionalFiles.Get(AdditionalTypeEnum.Books).FirstOrDefault(f => f.Extension == ".fb3"); - if (fb3File == default) { + if (fb3File == null) + { Config.Logger.LogInformation("Нет файла fb3. Сформировать файл книги невозможно"); - } else { - try { + } + else + { + try + { await using var stream = fb3File.GetStream(); var litresBook = await GetBook(stream); book.Chapters = await FillChapters(litresBook, art.Title); - } catch (Exception) { + } + catch (Exception) + { Config.Logger.LogInformation($"Не удалось обработать оригинальный файл {fb3File.FullName}"); } } @@ -172,105 +198,132 @@ public override async Task Get(Uri url) { return book; } - private async Task FillAdditional(Book book, LitresArt art) { - if (_authData != default) { + private async Task FillAdditional(Book book, LitresArt art) + { + if (_authData != null) + { var type = art.ArtType == LitresArtTypeEnum.Audio ? AdditionalTypeEnum.Audio : AdditionalTypeEnum.Books; - - if (Config.Options.HasAdditionalType(type)) { + + if (Config.Options.HasAdditionalType(type)) + { var files = await GetResponse($"https://api.litres.ru/foundation/api/arts/{art.Id}/files/grouped".AsUri()); - foreach (var file in files.SelectMany(f => f.Files)) { + foreach (var file in files.SelectMany(f => f.Files)) + { using var fileResponse = await GetFileResponse(art, file); - if (fileResponse != default) { + if (fileResponse != null) + { var tempFile = await CreateTempFile(fileResponse); book.AdditionalFiles.Add(art.ArtType != LitresArtTypeEnum.Audio ? AdditionalTypeEnum.Books : AdditionalTypeEnum.Audio, tempFile); } } - } else if (art.ArtType != LitresArtTypeEnum.Audio) { + } + else if (art.ArtType != LitresArtTypeEnum.Audio) + { using var fileResponse = await GetFileResponse(art, null); - if (fileResponse != default) { + if (fileResponse != null) + { var tempFile = await CreateTempFile(fileResponse); book.AdditionalFiles.Add(AdditionalTypeEnum.Books, tempFile); - } } } - if (_authData == default || (book.AdditionalFiles.Get(AdditionalTypeEnum.Books).Count == 0 && art.ArtType != LitresArtTypeEnum.Audio)) { + if (_authData == null || (book.AdditionalFiles.Get(AdditionalTypeEnum.Books).Count == 0 && art.ArtType != LitresArtTypeEnum.Audio)) + { var fileResponse = await GetShortBook(art); - if (fileResponse != default) { + if (fileResponse != null) + { book.AdditionalFiles.Add(AdditionalTypeEnum.Books, await CreateTempFile(fileResponse)); } } } - private async Task CreateTempFile(HttpResponseMessage response) { - return await TempFile.Create(response.RequestMessage.RequestUri, Config.TempFolder.Path, response.Content.Headers.ContentDisposition?.FileName?.Trim('\"') ?? response.RequestMessage.RequestUri.GetFileName(), await response.Content.ReadAsStreamAsync()); + private async Task CreateTempFile(HttpResponseMessage response) + { + return await TempFile.Create( + response.RequestMessage.RequestUri, + Config.TempFolder.Path, + response.Content.Headers.ContentDisposition?.FileName?.Trim('\"') ?? response.RequestMessage.RequestUri.GetFileName(), + await response.Content.ReadAsStreamAsync()); } - private static string GetBookId(Uri url) { + private static string GetBookId(Uri url) + { var art = url.GetQueryParameter("art"); - if (!string.IsNullOrWhiteSpace(art)) { + if (!string.IsNullOrWhiteSpace(art)) + { return art; } - + art = url.GetSegment(url.Segments.Length - 1).Split("-").Last(); - if (long.TryParse(art, out _)) { + if (long.TryParse(art, out _)) + { return art; } throw new Exception("Не удалось определить artId"); } - private Seria GetSeria(LitresArt art) { + private Seria GetSeria(LitresArt art) + { return art .Sequences - .Select(s => new Seria { - Name = s.Name, - Url = SystemUrl.MakeRelativeUri("serii-knig/").AppendQueryParameter("id", s.Id), - Number = s.SequenceNumber + .Select(s => new Seria + { + Name = s.Name, Url = SystemUrl.MakeRelativeUri("serii-knig/").AppendQueryParameter("id", s.Id), Number = s.SequenceNumber }).FirstOrDefault(); } - - private async Task GetAuthor(LitresArt art) { + + private async Task GetAuthor(LitresArt art) + { var person = art.Persons.FirstOrDefault(a => a.Role == "author"); - var author = person == null ? default : await GetResponse>($"https://api.litres.ru/foundation/api/persons/{person.Id}".AsUri()); - return author == default ? - new Author("Litres") : + var author = person == null ? null : await GetResponse>($"https://api.litres.ru/foundation/api/persons/{person.Id}".AsUri()); + return author == null ? + new Author("Litres") : new Author(author.FullName, SystemUrl.MakeRelativeUri(author.Url)); } - - private async Task> GetCoAuthors(LitresArt art) { + + private async Task> GetCoAuthors(LitresArt art) + { var result = new List(); - foreach (var person in art.Persons.Where(a => a.Role == "author").Skip(1)) { - var author = person == null ? default : await GetResponse>($"https://api.litres.ru/foundation/api/persons/{person.Id}".AsUri()); - if (author != default) { + foreach (var person in art.Persons.Where(a => a.Role == "author").Skip(1)) + { + var author = person == null ? null : await GetResponse>($"https://api.litres.ru/foundation/api/persons/{person.Id}".AsUri()); + if (author != null) + { result.Add(new Author(author.FullName, SystemUrl.MakeRelativeUri(author.Url))); } } return result; } - - private Task GetCover(string imagePath) { + + private Task GetCover(string imagePath) + { return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(SystemUrl.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - private async Task> FillChapters(LitresBook book, string title) { + private async Task> FillChapters(LitresBook book, string title) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - - foreach (var section in book.Content.QuerySelectorAll("section")) { - if (section.QuerySelector("> section") != null) { + + foreach (var section in book.Content.QuerySelectorAll("section")) + { + if (section.QuerySelector("> section") != null) + { continue; } - - var chapter = new Chapter { + + var chapter = new Chapter + { Title = (section.GetTextBySelector("title") ?? title).ReplaceNewLine() }; - + section.RemoveNodes("title, note, clipped"); chapter.Images = await GetImages(section, book); chapter.Content = section.InnerHtml; @@ -280,21 +333,26 @@ private async Task> FillChapters(LitresBook book, string ti return result; } - private async Task> GetImages(HtmlNode doc, LitresBook book) { + private async Task> GetImages(HtmlNode doc, LitresBook book) + { var images = new List(); - foreach (var img in doc.QuerySelectorAll("img")) { + foreach (var img in doc.QuerySelectorAll("img")) + { var path = img.Attributes["src"]?.Value; - if (string.IsNullOrWhiteSpace(path)) { + if (string.IsNullOrWhiteSpace(path)) + { img.Remove(); continue; } - - if (!book.Targets.TryGetValue(path, out var t)) { + + if (!book.Targets.TryGetValue(path, out var t)) + { img.Remove(); continue; } - - if (t?.Content == null || t.Content.Length == 0) { + + if (t?.Content == null || t.Content.Length == 0) + { img.Remove(); continue; } @@ -308,19 +366,34 @@ private async Task> GetImages(HtmlNode doc, LitresBook boo return images; } - private async Task GetFileResponse(LitresArt art, LitresFile file) { - var paths = new []{"download_book_j", "download_book_subscr", "download_my_book_j"}; - var hosts = new List { null, SystemUrl.Host }; - if (_me?.PartnerSubscriptions?.Subscriptions != default) { - hosts.AddRange(_me.PartnerSubscriptions.Subscriptions.Where(s => s.IsActive && s.Type == (art.ArtType == LitresArtTypeEnum.Audio ? "audio" : "text")).Select(s => s.Host).Distinct()); + private async Task GetFileResponse(LitresArt art, LitresFile file) + { + var paths = new[] + { + "download_book_j", "download_book_subscr", "download_my_book_j" + }; + + var hosts = new List + { + null, SystemUrl.Host + }; + + if (_me?.PartnerSubscriptions?.Subscriptions != null) + { + hosts.AddRange( + _me.PartnerSubscriptions.Subscriptions.Where(s => s.IsActive && s.Type == (art.ArtType == LitresArtTypeEnum.Audio ? "audio" : "text")).Select(s => s.Host).Distinct()); } - if (_authData != null) { - foreach (var host in hosts) { - foreach (var path in paths) { + if (_authData != null) + { + foreach (var host in hosts) + { + foreach (var path in paths) + { var uri = GetFullUri(art, path, file, host); var response = await Config.Client.GetAsync(uri); - if (response.StatusCode == HttpStatusCode.OK && response.Headers.AcceptRanges.Count != 0) { + if (response.StatusCode == HttpStatusCode.OK && response.Headers.AcceptRanges.Count != 0) + { Config.Logger.LogInformation($"Дополнительный файл доступен по ссылке {uri}"); return response; } @@ -328,46 +401,55 @@ private async Task GetFileResponse(LitresArt art, LitresFil } } - return default; + return null; } - private async Task GetShortBook(LitresArt art) { + private async Task GetShortBook(LitresArt art) + { var shortUri = GetShortUri(art); Config.Logger.LogInformation($"Дополнительный файл доступен по ссылке {shortUri}"); return await Config.Client.GetAsync(shortUri); } - private async Task GetBook(Stream stream) { + private async Task GetBook(Stream stream) + { var result = new LitresBook(); - + var map = new Dictionary(); using var zip = new ZipArchive(stream); - foreach (var entry in zip.Entries) { + foreach (var entry in zip.Entries) + { using var ms = new MemoryStream(); await entry.Open().CopyToAsync(ms); map[entry.FullName.Replace("fb3/", string.Empty)] = ms.ToArray(); } - foreach (var (key, value) in map) { - if (key.EndsWith("body.xml")) { + foreach (var (key, value) in map) + { + if (key.EndsWith("body.xml")) + { result.Content = Encoding.UTF8.GetString(value).AsXHtmlDoc(); - } else if (key.EndsWith("body.xml.rels")) { - foreach (var r in Encoding.UTF8.GetString(value).AsXHtmlDoc().QuerySelectorAll("Relationship")) { + } + else if (key.EndsWith("body.xml.rels")) + { + foreach (var r in Encoding.UTF8.GetString(value).AsXHtmlDoc().QuerySelectorAll("Relationship")) + { var id = r.Attributes["Id"]?.Value; var target = r.Attributes["Target"]?.Value; - if (string.IsNullOrWhiteSpace(id) || string.IsNullOrWhiteSpace(target)) { + if (string.IsNullOrWhiteSpace(id) || string.IsNullOrWhiteSpace(target)) + { continue; } - if (!map.TryGetValue(target, out var t)) { + if (!map.TryGetValue(target, out var t)) + { continue; } - - result.Targets[id] = new LitresTarget { - Id = id, - Target = target, - Content = t + + result.Targets[id] = new LitresTarget + { + Id = id, Target = target, Content = t }; } } @@ -375,4 +457,4 @@ private async Task GetBook(Stream stream) { return result; } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/LitsovetGetter.cs b/src/Core/Logic/Getters/LitsovetGetter.cs similarity index 69% rename from Core/Logic/Getters/LitsovetGetter.cs rename to src/Core/Logic/Getters/LitsovetGetter.cs index 90009993..a40ca485 100644 --- a/Core/Logic/Getters/LitsovetGetter.cs +++ b/src/Core/Logic/Getters/LitsovetGetter.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; using System.Net.Http.Json; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -13,83 +8,110 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class LitsovetGetter(BookGetterConfig config) : GetterBase(config) { +public class LitsovetGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://litsovet.ru/"); - protected override string GetId(Uri url) => url.GetSegment(2); - public override async Task Authorize() { - if (!Config.HasCredentials) { + protected override string GetId(Uri url) + => url.GetSegment(2); + + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } - + using var post = await Config.Client.PostAsync(SystemUrl.MakeRelativeUri("/ajax/login_signup_remind"), GenerateAuthData()); var response = await post.Content.ReadFromJsonAsync(); - if (response.Ok == 1) { + if (response.Ok == 1) + { Config.Logger.LogInformation("Успешно авторизовались"); - } else { + } + else + { throw new Exception("Не удалось авторизоваться."); } } - private FormUrlEncodedContent GenerateAuthData() { - var payload = new Dictionary { - { "action", "login" }, - { "email", Config.Options.Login }, - { "password", Config.Options.Password }, + private FormUrlEncodedContent GenerateAuthData() + { + var payload = new Dictionary + { + { + "action", "login" + }, + { + "email", Config.Options.Login + }, + { + "password", Config.Options.Password + }, }; return new FormUrlEncodedContent(payload); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri("/books/" + GetId(url)); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(url), Title = doc.GetTextBySelector("h1"), Author = GetAuthor(doc), Annotation = doc.QuerySelector("div.book-informate div.item-descr")?.InnerHtml }; - + return book; } - private Author GetAuthor(HtmlDocument doc) { + private Author GetAuthor(HtmlDocument doc) + { var a = doc.QuerySelector("div.p-book-author a"); return new Author(a.QuerySelector("span").GetText(), SystemUrl.MakeRelativeUri(a.Attributes["href"].Value)); } - - private async Task> GetToc(Uri url) { + + private async Task> GetToc(Uri url) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(url.AppendSegment("/read/")); - + var result = new List(); - foreach (var a in doc.QuerySelectorAll("div.book-navi-list li a")) { + foreach (var a in doc.QuerySelectorAll("div.book-navi-list li a")) + { var href = a.Attributes["onclick"]?.Value?.Split(" ").FirstOrDefault(s => s.Contains("books"))?.Trim('\''); - result.Add(string.IsNullOrEmpty(href) - ? new UrlChapter(null, a.GetText().ReplaceNewLine()) - : new UrlChapter(url.MakeRelativeUri(href), a.GetText().ReplaceNewLine())); + result.Add( + string.IsNullOrEmpty(href) + ? new UrlChapter(null, a.GetText().ReplaceNewLine()) + : new UrlChapter(url.MakeRelativeUri(href), a.GetText().ReplaceNewLine())); } - + return SliceToc(result, c => c.Title); } - private async Task> FillChapters(Uri url) { + private async Task> FillChapters(Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - - foreach (var urlChapter in await GetToc(url)) { + + foreach (var urlChapter in await GetToc(url)) + { Config.Logger.LogInformation($"Загружаю главу {urlChapter.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = urlChapter.Title }; - if (urlChapter.Url != default) { + if (urlChapter.Url != null) + { var chapterDoc = await GetChapter(urlChapter); chapter.Images = await GetImages(chapterDoc, urlChapter.Url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; @@ -101,13 +123,15 @@ private async Task> FillChapters(Uri url) { return result; } - private async Task GetChapter(UrlChapter urlChapter) { + private async Task GetChapter(UrlChapter urlChapter) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(urlChapter.Url); return doc.QuerySelector("div.reader").InnerHtml.AsHtmlDoc(); } - private Task GetCover(HtmlDocument doc, Uri bookUri) { + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("div.p-book-cover img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/MangaMammyGetter.cs b/src/Core/Logic/Getters/MangaMammyGetter.cs similarity index 78% rename from Core/Logic/Getters/MangaMammyGetter.cs rename to src/Core/Logic/Getters/MangaMammyGetter.cs index 3e15834f..befe5a7e 100644 --- a/Core/Logic/Getters/MangaMammyGetter.cs +++ b/src/Core/Logic/Getters/MangaMammyGetter.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -13,67 +9,79 @@ namespace Core.Logic.Getters; -public class MangaMammyGetter(BookGetterConfig config) : GetterBase(config) { +public class MangaMammyGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://mangamammy.ru/"); - - protected override string GetId(Uri url) => url.GetSegment(2); - - public override async Task Get(Uri url) { + + protected override string GetId(Uri url) + => url.GetSegment(2); + + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri("/manga/" + GetId(url)); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("h1"), Author = GetAuthor(doc, url), Annotation = doc.QuerySelector("div.manga-excerpt")?.InnerHtml }; - + return book; } - private static Author GetAuthor(HtmlDocument doc, Uri url) { + private static Author GetAuthor(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("div.author-content a"); - return a == default ? new Author("MangaMammy") : new Author(a.GetText(), url.MakeRelativeUri(a.Attributes["href"].Value)); + return a == null ? new Author("MangaMammy") : new Author(a.GetText(), url.MakeRelativeUri(a.Attributes["href"].Value)); } - private async Task> FillChapters(HtmlDocument doc, Uri url) { + private async Task> FillChapters(HtmlDocument doc, Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - - foreach (var urlChapter in GetToc(doc, url)) { + + foreach (var urlChapter in GetToc(doc, url)) + { Config.Logger.LogInformation($"Загружаю главу {urlChapter.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = urlChapter.Title }; var chapterDoc = await GetChapter(urlChapter); - if (chapterDoc != default) { + if (chapterDoc != null) + { chapter.Images = await GetImages(chapterDoc, url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } - + result.Add(chapter); } return result; } - private IEnumerable GetToc(HtmlDocument doc, Uri url) { + private IEnumerable GetToc(HtmlDocument doc, Uri url) + { var result = doc .QuerySelectorAll("ul.sub-chap-list li.wp-manga-chapter > a") .Select(a => new UrlChapter(url.MakeRelativeUri(a.Attributes["href"].Value), a.GetText().ReplaceNewLine())) .Reverse() .ToList(); - + return SliceToc(result, c => c.Title); } - private async Task GetChapter(UrlChapter urlChapter) { + private async Task GetChapter(UrlChapter urlChapter) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(urlChapter.Url); var images = doc @@ -91,8 +99,9 @@ private async Task GetChapter(UrlChapter urlChapter) { return sb.AsHtmlDoc(); } - private Task GetCover(HtmlDocument doc, Uri bookUri) { + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("div.summary_image img")?.Attributes["data-lazy-src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/MirKnigGetter.cs b/src/Core/Logic/Getters/MirKnigGetter.cs similarity index 72% rename from Core/Logic/Getters/MirKnigGetter.cs rename to src/Core/Logic/Getters/MirKnigGetter.cs index d7fa66cd..8b9b668d 100644 --- a/Core/Logic/Getters/MirKnigGetter.cs +++ b/src/Core/Logic/Getters/MirKnigGetter.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -12,75 +8,87 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class MirKnigGetter(BookGetterConfig config) : GetterBase(config) { +public class MirKnigGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://mir-knig.com/"); - protected override string GetId(Uri url) => base.GetId(url).Split('_').Last().Split('-').First(); + protected override string GetId(Uri url) + => base.GetId(url).Split('_').Last().Split('-').First(); - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var bookId = GetId(url); url = SystemUrl.MakeRelativeUri($"/view_{bookId}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var title = doc.GetTextBySelector("h1.heading"); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(bookId, title), Title = title, Author = GetAuthor(doc, url), Annotation = doc.QuerySelector("div.desc")?.InnerHtml }; - + return book; } - private async Task> FillChapters(string bookId, string title) { - if (Config.Options.NoChapters) { + private async Task> FillChapters(string bookId, string title) + { + if (Config.Options.NoChapters) + { return []; } - + var doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri($"/read_{bookId}-1")); var pages = int.Parse(doc.QuerySelectorAll("select.allp option").Last().Attributes["value"].Value); var sb = new StringBuilder(); - for (var i = 1; i <= pages; i++) { + for (var i = 1; i <= pages; i++) + { Config.Logger.LogInformation($"Получаю страницу {i}/{pages}"); doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri($"/read_{bookId}-{i}")); sb.Append(doc.QuerySelector("div.text-block").InnerHtml.HtmlDecode()); } doc = sb.AsHtmlDoc(); - var chapter = new Chapter { - Title = title, - Images = await GetImages(doc, SystemUrl), - Content = doc.DocumentNode.InnerHtml + var chapter = new Chapter + { + Title = title, Images = await GetImages(doc, SystemUrl), Content = doc.DocumentNode.InnerHtml }; - return new []{chapter}; + return new[] + { + chapter + }; } - private static Author GetAuthor(HtmlDocument doc, Uri url) { + private static Author GetAuthor(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("a.owner"); return new Author(a.GetText(), url.MakeRelativeUri(a.Attributes["href"].Value)); } - - private Task GetCover(HtmlDocument doc, Uri bookUri) { + + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var thumb = doc.QuerySelector("div.cover div.thumb"); var imagePath = string.Empty; - if (thumb != default) { + if (thumb != null) + { var style = thumb.Attributes["style"]?.Value; - if (!string.IsNullOrWhiteSpace(style)) { + if (!string.IsNullOrWhiteSpace(style)) + { var match = Regex.Match(style, @"url\((?.*?)\)"); - if (match.Success) { + if (match.Success) + { imagePath = match.Groups["url"].Value; } } } - + return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - - -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/MyBookGetter.cs b/src/Core/Logic/Getters/MyBookGetter.cs similarity index 74% rename from Core/Logic/Getters/MyBookGetter.cs rename to src/Core/Logic/Getters/MyBookGetter.cs index 8c1d472a..19af8887 100644 --- a/Core/Logic/Getters/MyBookGetter.cs +++ b/src/Core/Logic/Getters/MyBookGetter.cs @@ -1,14 +1,8 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Net; -using System.Net.Http; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Nodes; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Misc; @@ -20,9 +14,10 @@ using Microsoft.Extensions.Logging; using OAuth; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class MyBookGetter(BookGetterConfig config) : GetterBase(config) { +public class MyBookGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://mybook.ru/"); private const string CONSUMER_KEY = "830968793b2a44c688400319f4a77231"; @@ -30,16 +25,19 @@ public class MyBookGetter(BookGetterConfig config) : GetterBase(config) { private const string IDENTIFIER = "47b17a4c8231a28f06c6c836d0b5f6f2134cb74d"; private HttpClient _apiClient; - private MyBookAuth _token = new() { - Secret = "fYFPndYhOTW6YxIJ", - Token = "f8d32ef906664ed3a1525b7298aac461" + + private MyBookAuth _token = new() + { + Secret = "fYFPndYhOTW6YxIJ", Token = "f8d32ef906664ed3a1525b7298aac461" }; - private Uri GetMainUrl(Uri uri) { + private Uri GetMainUrl(Uri uri) + { return SystemUrl.MakeRelativeUri($"{uri.GetSegment(1)}/{uri.GetSegment(2)}/{uri.GetSegment(3)}"); } - public override Task Init() { + public override Task Init() + { _apiClient = new HttpClient(); _apiClient.Timeout = Config.Client.Timeout; _apiClient.DefaultRequestHeaders.Add("User-Agent", "MyBook/6.6.0 (iPhone; iOS 16.0.3; Scale/3.00)"); @@ -47,8 +45,10 @@ public override Task Init() { return Task.CompletedTask; } - private void SetAuthHeader(string method, Uri uri) { - var client = new OAuthRequest { + private void SetAuthHeader(string method, Uri uri) + { + var client = new OAuthRequest + { Method = method, SignatureMethod = OAuthSignatureMethod.HmacSha1, ConsumerKey = CONSUMER_KEY, @@ -58,36 +58,41 @@ private void SetAuthHeader(string method, Uri uri) { Token = _token.Token, TokenSecret = _token.Secret }; - + _apiClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("OAuth", client.GetAuthorizationHeader().Replace("OAuth", string.Empty).Trim()); } - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } - + var authUrl = SystemUrl.MakeRelativeUri("/api/auth/"); SetAuthHeader("POST", authUrl); - var payload = new { - identifier = IDENTIFIER, - email = Config.Options.Login, - password = Config.Options.Password + var payload = new + { + identifier = IDENTIFIER, email = Config.Options.Login, password = Config.Options.Password }; - + using var response = await _apiClient.PostAsJsonAsync(authUrl, payload); - if (response.StatusCode == HttpStatusCode.OK) { + if (response.StatusCode == HttpStatusCode.OK) + { Config.Logger.LogInformation("Успешно авторизовались"); - } else { + } + else + { var message = await response.Content.ReadAsStringAsync(); throw new Exception($"Не удалось авторизоваться. {message}"); } - + _token = await response.Content.ReadFromJsonAsync(); } - - private static T GetNextData(HtmlDocument doc, string node) { + + private static T GetNextData(HtmlDocument doc, string node) + { var json = doc.QuerySelector("#__NEXT_DATA__").InnerText; return JsonDocument.Parse(json) .RootElement.GetProperty("props") @@ -98,13 +103,16 @@ private static T GetNextData(HtmlDocument doc, string node) { .Deserialize(); } - private async Task GetDetails(Uri url) { + private async Task GetDetails(Uri url) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var details = GetNextData(doc, "book"); - if (details.Type == "audio") { - var bookId = details.Connected?.Id ?? details.MapFiles.FirstOrDefault(b => b.Book != default)?.Book; - if (bookId.HasValue) { + if (details.Type == "audio") + { + var bookId = details.Connected?.Id ?? details.MapFiles.FirstOrDefault(b => b.Book != 0)?.Book; + if (bookId.HasValue) + { return await GetDetailsApi(bookId.Value); } } @@ -112,60 +120,68 @@ private async Task GetDetails(Uri url) { return details; } - private async Task GetDetailsApi(long bookId) { + private async Task GetDetailsApi(long bookId) + { var apiUrl = SystemUrl.MakeRelativeUri($"/api/v1/books/{bookId}/"); SetAuthHeader("GET", apiUrl); return await _apiClient.GetFromJsonAsync(apiUrl); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = GetMainUrl(url); var details = await GetDetails(url); - - var book = new Book(url) { - Cover = await GetCover(details), - Title = details.Name, - Author = GetAuthor(details), - Annotation = details.Annotation, + + var book = new Book(url) + { + Cover = await GetCover(details), Title = details.Name, Author = GetAuthor(details), Annotation = details.Annotation, }; - + var bookUrl = SystemUrl.MakeRelativeUri(details.BookFile); - + SetAuthHeader("GET", bookUrl); - if (details.Type != "audio") { + if (details.Type != "audio") + { using var response = await _apiClient.GetAsync(bookUrl); - if (response.StatusCode != HttpStatusCode.OK) { + if (response.StatusCode != HttpStatusCode.OK) + { throw new Exception("Не удалось получить книгу"); } var origBook = await TempFile.Create(bookUrl, Config.TempFolder.Path, bookUrl.GetFileName(), await response.Content.ReadAsStreamAsync()); - if (Config.Options.HasAdditionalType(AdditionalTypeEnum.Books)) { + if (Config.Options.HasAdditionalType(AdditionalTypeEnum.Books)) + { book.AdditionalFiles.Add(AdditionalTypeEnum.Books, origBook); } book.Chapters = await FillChaptersFromEpub(origBook); } - if (Config.Options.HasAdditionalType(AdditionalTypeEnum.Audio)) { - if (details.Type == "audio") { + if (Config.Options.HasAdditionalType(AdditionalTypeEnum.Audio)) + { + if (details.Type == "audio") + { book.AdditionalFiles.Add(AdditionalTypeEnum.Audio, await GetAudio(details.Id)); } - - if (details.Connected is { Type: "audio" }) { + + if (details.Connected is { Type: "audio" }) + { book.AdditionalFiles.Add(AdditionalTypeEnum.Audio, await GetAudio(details.Connected.Id)); } } - + return book; } - private async Task> GetAudio(long bookId) { + private async Task> GetAudio(long bookId) + { var result = new List(); var details = await GetDetailsApi(bookId); var files = details.Files.Where(node => node is JsonObject).Select(node => node.Deserialize()).ToList(); - for (var i = 0; i < files.Count; i++) { + for (var i = 0; i < files.Count; i++) + { var file = files[i]; var url = SystemUrl.MakeRelativeUri(file.Url); @@ -174,30 +190,34 @@ private async Task> GetAudio(long bookId) { var name = file.Title; var ext = Path.GetExtension(url.GetFileName()); - + using var response = await _apiClient.GetAsync(url); result.Add(await TempFile.Create(url, Config.TempFolder.Path, $"{file.Order}_{name}{ext}", await response.Content.ReadAsStreamAsync())); Config.Logger.LogInformation($"Аудиофайл файл {i + 1}/{files.Count} {url} загружен"); } - + return result; } - private Task GetCover(MyBookBook book) { + private Task GetCover(MyBookBook book) + { return !string.IsNullOrWhiteSpace(book.Cover) ? SaveImage($"https://i3.mybook.io/p/x378/{book.Cover.TrimStart('/')}".AsUri()) : Task.FromResult(default(TempFile)); } - - private Author GetAuthor(MyBookBook book) { - if (book.Authors.Length == 0) { + + private Author GetAuthor(MyBookBook book) + { + if (book.Authors.Length == 0) + { return new Author("MyBook"); } var author = book.Authors[0]; return string.IsNullOrWhiteSpace(author.Url) ? new Author(author.Name) : new Author(author.Name, SystemUrl.MakeRelativeUri(author.Url)); } - - public override void Dispose() { + + public override void Dispose() + { base.Dispose(); _apiClient?.Dispose(); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/NeobookGetter.cs b/src/Core/Logic/Getters/NeobookGetter.cs similarity index 63% rename from Core/Logic/Getters/NeobookGetter.cs rename to src/Core/Logic/Getters/NeobookGetter.cs index 11e39e14..94f44407 100644 --- a/Core/Logic/Getters/NeobookGetter.cs +++ b/src/Core/Logic/Getters/NeobookGetter.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; using System.Net; -using System.Net.Http; using System.Net.Http.Json; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -15,23 +11,28 @@ namespace Core.Logic.Getters; -public class NeobookGetter(BookGetterConfig config) : GetterBase(config) { +public class NeobookGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("http://neobook.org/"); private Uri _apiUrl = new("https://api.neobook.org/"); - protected override string GetId(Uri url) { + protected override string GetId(Uri url) + { return url.GetSegment(1) == "book" ? url.GetSegment(2) : url.GetQueryParameter("book"); } - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } var response = await Config.Client.PostWithTriesAsync(_apiUrl, GenerateAuthData()); var data = await response.Content.ReadFromJsonAsync(); - if (!string.IsNullOrWhiteSpace(data?.Error)) { + if (!string.IsNullOrWhiteSpace(data?.Error)) + { throw new Exception($"Не удалось авторизоваться. {data.Error}"); } @@ -40,26 +41,46 @@ public override async Task Authorize() { Config.CookieContainer.Add(SystemUrl, new Cookie("uid", data?.Login.Uid)); } - private MultipartFormDataContent GenerateAuthData() { - return new() { - { new StringContent("3.1"), "version" }, - { new StringContent("0"), "uid" }, - { new StringContent(string.Empty), "utoken" }, - { new StringContent("authorization"), "resource" }, - { new StringContent("login_by_email"), "action" }, - { new StringContent(Config.Options.Login), "email" }, - { new StringContent(Config.Options.Password), "password" }, - { new StringContent("3"), "platform_id" }, + private MultipartFormDataContent GenerateAuthData() + { + return new() + { + { + new StringContent("3.1"), "version" + }, + { + new StringContent("0"), "uid" + }, + { + new StringContent(string.Empty), "utoken" + }, + { + new StringContent("authorization"), "resource" + }, + { + new StringContent("login_by_email"), "action" + }, + { + new StringContent(Config.Options.Login), "email" + }, + { + new StringContent(Config.Options.Password), "password" + }, + { + new StringContent("3"), "platform_id" + }, }; } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var bookId = GetId(url); url = SystemUrl.MakeRelativeUri("/book/" + bookId + "/"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var data = GetPostData(doc); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(data), Chapters = await FillChapters(data, bookId), Title = data.Title, @@ -70,29 +91,36 @@ public override async Task Get(Uri url) { return book; } - private static NeobookPostData GetPostData(HtmlDocument doc) { + private static NeobookPostData GetPostData(HtmlDocument doc) + { var data = Regex.Match(doc.ParsedText, "var postData = (?.*?);\n").Groups["data"].Value; return data.Deserialize(); } - private Author GetAuthor(NeobookPostData data) { + private Author GetAuthor(NeobookPostData data) + { return new Author(data.User.LastName + " " + data.User.FirstName, SystemUrl.MakeRelativeUri(data.User.UserName)); } - private async Task> FillChapters(NeobookPostData data, string bookId) { + private async Task> FillChapters(NeobookPostData data, string bookId) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var neobookChapter in SliceToc(data.Chapters, c => c.Title)) { + foreach (var neobookChapter in SliceToc(data.Chapters, c => c.Title)) + { Config.Logger.LogInformation($"Загружаю главу {neobookChapter.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = neobookChapter.Title }; - + var chapterDoc = await GetChapter(neobookChapter, bookId); - if (chapterDoc != default) { + if (chapterDoc != null) + { chapter.Images = await GetImages(chapterDoc, SystemUrl); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } @@ -103,28 +131,35 @@ private async Task> FillChapters(NeobookPostData data, stri return result; } - private async Task GetChapter(NeobookTocChapter neobookTocChapter, string bookId) { - var doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri("reader/").AppendQueryParameter("book", bookId).AppendQueryParameter("chapter", neobookTocChapter.Token)); + private async Task GetChapter(NeobookTocChapter neobookTocChapter, string bookId) + { + var doc = await Config.Client.GetHtmlDocWithTriesAsync( + SystemUrl.MakeRelativeUri("reader/").AppendQueryParameter("book", bookId).AppendQueryParameter("chapter", neobookTocChapter.Token)); + var data = Regex.Match(doc.ParsedText, "var data = (?.*?);\n").Groups["data"].Value; - if (string.IsNullOrWhiteSpace(data)) { - return default; + if (string.IsNullOrWhiteSpace(data)) + { + return null; } - + var neobookBook = data.Deserialize(); - if (neobookBook.ActiveChapterIndex > neobookBook.Chapters.Length) { - return default; + if (neobookBook.ActiveChapterIndex > neobookBook.Chapters.Length) + { + return null; } var chapter = neobookBook.Chapters[neobookBook.ActiveChapterIndex]; - if (string.IsNullOrWhiteSpace(chapter.Data?.Html)) { - return default; + if (string.IsNullOrWhiteSpace(chapter.Data?.Html)) + { + return null; } - + return chapter.Data.Html.AsHtmlDoc(); } - private Task GetCover(NeobookPostData data) { + private Task GetCover(NeobookPostData data) + { var imagePath = data.Attachment?.Cover?.GetValueOrDefault("l"); return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(imagePath.AsUri()) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/NovelTranslateGetter.cs b/src/Core/Logic/Getters/NovelTranslateGetter.cs similarity index 76% rename from Core/Logic/Getters/NovelTranslateGetter.cs rename to src/Core/Logic/Getters/NovelTranslateGetter.cs index b2260d34..bdc0b148 100644 --- a/Core/Logic/Getters/NovelTranslateGetter.cs +++ b/src/Core/Logic/Getters/NovelTranslateGetter.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -9,14 +6,18 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class NovelTranslateGetter(BookGetterConfig config) : GetterBase(config) { +public class NovelTranslateGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://noveltranslate.com/"); - protected override string GetId(Uri url) { - for (var i = 0; i < url.Segments.Length; i++) { - if (url.Segments[i].StartsWith("novel")) { + protected override string GetId(Uri url) + { + for (var i = 0; i < url.Segments.Length; i++) + { + if (url.Segments[i].StartsWith("novel")) + { return url.Segments[i + 1].Trim('/'); } } @@ -24,18 +25,21 @@ protected override string GetId(Uri url) { throw new Exception($"Не могу определить ID книги {url}"); } - private static string GetLang(Uri url) { + private static string GetLang(Uri url) + { var lang = url.Segments[1].Trim('/'); return lang == "novel" ? string.Empty : lang; } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var lang = GetLang(url); url = string.IsNullOrWhiteSpace(lang) ? SystemUrl.MakeRelativeUri($"/novel/{GetId(url)}/") : SystemUrl.MakeRelativeUri($"/{lang}/novel/{GetId(url)}/"); - + var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, lang, url), Title = doc.GetTextBySelector("h1"), @@ -43,23 +47,27 @@ public override async Task Get(Uri url) { Annotation = GetAnnotation(doc), Lang = lang }; - + return book; } - private async Task> FillChapters(HtmlDocument doc, string lang, Uri url) { + private async Task> FillChapters(HtmlDocument doc, string lang, Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var urlChapter in await GetToc(doc, lang, url)) { + foreach (var urlChapter in await GetToc(doc, lang, url)) + { Config.Logger.LogInformation($"Загружаю главу {urlChapter.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = urlChapter.Title }; - + var chapterDoc = await GetChapter(urlChapter); chapter.Images = await GetImages(chapterDoc, SystemUrl); chapter.Content = chapterDoc.DocumentNode.InnerHtml; @@ -70,20 +78,24 @@ private async Task> FillChapters(HtmlDocument doc, string l return result; } - private async Task GetChapter(UrlChapter urlChapter) { + private async Task GetChapter(UrlChapter urlChapter) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(urlChapter.Url); return doc.QuerySelector("div.reading-content div.text-left").RemoveNodes("div.novel-before-content, div.tptn_counter, div.novel-after-content").InnerHtml.AsHtmlDoc(); } - private async Task> GetToc(HtmlDocument doc, string lang, Uri url) { + private async Task> GetToc(HtmlDocument doc, string lang, Uri url) + { var lastChapter = url.MakeRelativeUri(doc.QuerySelector("div.listing-chapters_wrap li.wp-manga-chapter a").Attributes["href"].Value); var chapterDoc = await Config.Client.GetHtmlDocWithTriesAsync(lastChapter); var chapters = new List(); - foreach (var option in chapterDoc.QuerySelector("select.selectpicker_chapter").QuerySelectorAll("option")) { + foreach (var option in chapterDoc.QuerySelector("select.selectpicker_chapter").QuerySelectorAll("option")) + { var name = option.GetText(); var chapterUri = option.Attributes["data-redirect"].Value.AsUri(); - if (!string.IsNullOrWhiteSpace(lang) && !chapterUri.LocalPath.Trim('/').StartsWith(lang)) { + if (!string.IsNullOrWhiteSpace(lang) && !chapterUri.LocalPath.Trim('/').StartsWith(lang)) + { chapterUri = url.MakeRelativeUri($"/{lang}/{chapterUri.LocalPath.Trim('/')}"); } @@ -94,17 +106,20 @@ private async Task> GetToc(HtmlDocument doc, string lang return SliceToc(chapters, c => c.Title); } - private static string GetAnnotation(HtmlDocument doc) { + private static string GetAnnotation(HtmlDocument doc) + { return doc.QuerySelector("div.description-summary div.summary__content > p")?.InnerHtml; } - private Task GetCover(HtmlDocument doc, Uri uri) { + private Task GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("div.summary_image img")?.Attributes["data-lazy-src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - - private static Author GetAuthor(HtmlDocument doc, Uri uri) { + + private static Author GetAuthor(HtmlDocument doc, Uri uri) + { var a = doc.QuerySelector("div.summary-content div.author-content a"); - return a != default ? new Author(a.GetText(), uri.MakeRelativeUri(a.Attributes["href"].Value)) : new Author("NovelTranslate"); + return a != null ? new Author(a.GetText(), uri.MakeRelativeUri(a.Attributes["href"].Value)) : new Author("NovelTranslate"); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/NovelhallGetter.cs b/src/Core/Logic/Getters/NovelhallGetter.cs similarity index 76% rename from Core/Logic/Getters/NovelhallGetter.cs rename to src/Core/Logic/Getters/NovelhallGetter.cs index 08f8c591..5e00a686 100644 --- a/Core/Logic/Getters/NovelhallGetter.cs +++ b/src/Core/Logic/Getters/NovelhallGetter.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -12,18 +8,22 @@ namespace Core.Logic.Getters; -public class NovelhallGetter(BookGetterConfig config) : GetterBase(config) { +public class NovelhallGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://www.novelhall.com/"); - protected override string GetId(Uri url) { + protected override string GetId(Uri url) + { return url.GetSegment(1); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri(GetId(url)); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("h1"), @@ -31,23 +31,27 @@ public override async Task Get(Uri url) { Annotation = GetAnnotation(doc), Lang = "en" }; - + return book; } - private async Task> FillChapters(HtmlDocument doc, Uri url) { + private async Task> FillChapters(HtmlDocument doc, Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var urlChapter in GetToc(doc, url)) { + foreach (var urlChapter in GetToc(doc, url)) + { Config.Logger.LogInformation($"Загружаю главу {urlChapter.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = urlChapter.Title }; - + var chapterDoc = await GetChapter(urlChapter); chapter.Images = await GetImages(chapterDoc, SystemUrl); chapter.Content = chapterDoc.DocumentNode.InnerHtml; @@ -58,27 +62,32 @@ private async Task> FillChapters(HtmlDocument doc, Uri url) return result; } - private async Task GetChapter(UrlChapter urlChapter) { + private async Task GetChapter(UrlChapter urlChapter) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(urlChapter.Url); return doc.QuerySelector("div.entry-content").InnerHtml.AsHtmlDoc(); } - private IEnumerable GetToc(HtmlDocument doc, Uri url) { + private IEnumerable GetToc(HtmlDocument doc, Uri url) + { var chapters = doc.QuerySelectorAll("#morelist ul li a[href]").Select(a => new UrlChapter(url.MakeRelativeUri(a.Attributes["href"].Value), a.GetText())).ToList(); return SliceToc(chapters, c => c.Title); } - private static string GetAnnotation(HtmlDocument doc) { + private static string GetAnnotation(HtmlDocument doc) + { return doc.QuerySelector("div.intro span.js-close-wrap").RemoveNodes(".blue")?.InnerHtml; } - private Task GetCover(HtmlDocument doc, Uri uri) { + private Task GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("div.book-img img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - - private static Author GetAuthor(HtmlDocument doc) { + + private static Author GetAuthor(HtmlDocument doc) + { var a = doc.QuerySelector("meta[property='books:author']"); - return a != default ? new Author(a.Attributes["content"].Value) : new Author("NovelHall"); + return a != null ? new Author(a.Attributes["content"].Value) : new Author("NovelHall"); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Novelxo/NovelxoGetter.cs b/src/Core/Logic/Getters/Novelxo/NovelxoGetter.cs similarity index 68% rename from Core/Logic/Getters/Novelxo/NovelxoGetter.cs rename to src/Core/Logic/Getters/Novelxo/NovelxoGetter.cs index 93dfd1d1..9c89c0e1 100644 --- a/Core/Logic/Getters/Novelxo/NovelxoGetter.cs +++ b/src/Core/Logic/Getters/Novelxo/NovelxoGetter.cs @@ -1,8 +1,8 @@ -using System; using Core.Configs; -namespace Core.Logic.Getters.Novelxo; +namespace Core.Logic.Getters.Novelxo; -public class NovelxoGetter(BookGetterConfig config) : NovelxoGetterBase(config) { +public class NovelxoGetter(BookGetterConfig config) : NovelxoGetterBase(config) +{ protected override Uri SystemUrl => new("https://novelxo.com"); -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Novelxo/NovelxoGetterBase.cs b/src/Core/Logic/Getters/Novelxo/NovelxoGetterBase.cs similarity index 75% rename from Core/Logic/Getters/Novelxo/NovelxoGetterBase.cs rename to src/Core/Logic/Getters/Novelxo/NovelxoGetterBase.cs index 954e77c5..2287afa9 100644 --- a/Core/Logic/Getters/Novelxo/NovelxoGetterBase.cs +++ b/src/Core/Logic/Getters/Novelxo/NovelxoGetterBase.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -15,31 +9,37 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.Novelxo; +namespace Core.Logic.Getters.Novelxo; -public abstract class NovelxoGetterBase(BookGetterConfig config) : GetterBase(config) { +public abstract class NovelxoGetterBase(BookGetterConfig config) : GetterBase(config) +{ private static readonly byte[] Key = StringToByteArray("61626326312a7e235e325e2373305e3d295e5e3725623334"); private static readonly byte[] IV = StringToByteArray("31323334353637383930383533373237"); - protected override string GetId(Uri url) => url.GetSegment(1); + protected override string GetId(Uri url) + => url.GetSegment(1); - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = await GetMainUrl(url); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - - var book = new Book(url) { + + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("h1"), Author = GetAuthor(doc, url), Annotation = GetAnnotation(doc) }; - + return book; } - private async Task GetMainUrl(Uri url) { - if (url.Segments.Length == 2) { + private async Task GetMainUrl(Uri url) + { + if (url.Segments.Length == 2) + { return SystemUrl.MakeRelativeUri(GetId(url)); } @@ -47,55 +47,66 @@ private async Task GetMainUrl(Uri url) { return url.MakeRelativeUri(doc.QuerySelector("div.readerheader-wg a").Attributes["href"].Value); } - private async Task> FillChapters(HtmlDocument doc, Uri url) { + private async Task> FillChapters(HtmlDocument doc, Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - + var start = url.MakeRelativeUri(doc.QuerySelector("div.actions a.read").Attributes["href"].Value); - - while (true) { + + while (true) + { var sb = new StringBuilder(); doc = await Config.Client.GetHtmlDocWithTriesAsync(start); var container = doc.QuerySelector("div.readerbody-wg"); - foreach (var node in container.QuerySelectorAll("p, div.ctp")) { - if (node.Name == "p") { + foreach (var node in container.QuerySelectorAll("p, div.ctp")) + { + if (node.Name == "p") + { sb.Append(node.InnerHtml.HtmlDecode().CoverTag(node.Name)); - } else if (node.Name == "div" && node.Attributes["class"]?.Value?.Contains("ctp") == true) { - if (node.Attributes["class"].Value.Contains("protected")) { + } + else if (node.Name == "div" && node.Attributes["class"]?.Value?.Contains("ctp") == true) + { + if (node.Attributes["class"].Value.Contains("protected")) + { sb.Append(await GetProtected(doc, node)); - } else { + } + else + { sb.Append(Encoding.UTF8.GetString(Transform(Convert.FromBase64String(node.InnerText), Key, IV)).HtmlDecode()); } } } var chapterDoc = sb.AsHtmlDoc(); - var chapter = new Chapter { - Title = doc.GetTextBySelector("h2.chapter-title"), - Images = await GetImages(chapterDoc, start), - Content = chapterDoc.DocumentNode.InnerHtml + var chapter = new Chapter + { + Title = doc.GetTextBySelector("h2.chapter-title"), Images = await GetImages(chapterDoc, start), Content = chapterDoc.DocumentNode.InnerHtml }; result.Add(chapter); - + Config.Logger.LogInformation($"Загружена глава {chapter.Title.CoverQuotes()}"); var next = doc.QuerySelector("div.readernav-wg a.next"); - if (next == default) { + if (next == null) + { return result; } start = start.MakeRelativeUri(next.Attributes["href"].Value); } } - - private async Task GetProtected(HtmlDocument doc, HtmlNode node) { + + private async Task GetProtected(HtmlDocument doc, HtmlNode node) + { var id = node.Attributes["data-id"].Value; var header = Regex.Match(doc.ParsedText, @"http.setRequestHeader\((?.*?), '(?.*?)'"); - + var headerName = Regex.Match(doc.ParsedText, $"{header.Groups["name"].Value} = \'(?.*?)\'").Groups["id"].Value; var headerValue = header.Groups["value"].Value; var key = StringToByteArray(Regex.Match(doc.ParsedText, "signKey = \'(?.*?)\'").Groups["id"].Value); @@ -114,37 +125,43 @@ private async Task GetProtected(HtmlDocument doc, HtmlNode node) { return result; } - private static string Atob(string toEncode) { + private static string Atob(string toEncode) + { return Convert.ToBase64String(Encoding.GetEncoding(28591).GetBytes(toEncode)); } - private static string GetAnnotation(HtmlDocument doc) { + private static string GetAnnotation(HtmlDocument doc) + { var html = doc.QuerySelector("#bookinfo")?.InnerHtml; - return string.IsNullOrWhiteSpace(html) ? - string.Empty : + return string.IsNullOrWhiteSpace(html) ? + string.Empty : html.AsHtmlDoc().RemoveNodes("script, ins").DocumentNode.InnerHtml; } - - private Task GetCover(HtmlDocument doc, Uri uri) { + + private Task GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("div.cover img")?.Attributes["data-src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - - private static Author GetAuthor(HtmlDocument doc, Uri url) { + + private static Author GetAuthor(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("tr.authors a"); - return a == default ? - new Author("Novelxo") : + return a == null ? + new Author("Novelxo") : new Author(a.GetText(), url.MakeRelativeUri(a.Attributes["href"].Value)); } - private static byte[] StringToByteArray(string hex) { + private static byte[] StringToByteArray(string hex) + { return Enumerable.Range(0, hex.Length) .Where(x => x % 2 == 0) .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) .ToArray(); } - private static void AesCtrTransform(byte[] key, byte[] salt, Stream inputStream, Stream outputStream) { + private static void AesCtrTransform(byte[] key, byte[] salt, Stream inputStream, Stream outputStream) + { var aes = Aes.Create(); aes.Mode = CipherMode.ECB; aes.Padding = PaddingMode.None; @@ -157,19 +174,24 @@ private static void AesCtrTransform(byte[] key, byte[] salt, Stream inputStream, var counterEncryptor = aes.CreateEncryptor(key, zeroIv); int b; - while ((b = inputStream.ReadByte()) != -1) { - if (xorMask.Count == 0) { + while ((b = inputStream.ReadByte()) != -1) + { + if (xorMask.Count == 0) + { var counterModeBlock = new byte[blockSize]; counterEncryptor.TransformBlock(counter, 0, counter.Length, counterModeBlock, 0); - for (var i = counter.Length - 1; i >= 0; i--) { - if (++counter[i] != 0) { + for (var i = counter.Length - 1; i >= 0; i--) + { + if (++counter[i] != 0) + { break; } } - foreach (var b2 in counterModeBlock) { + foreach (var b2 in counterModeBlock) + { xorMask.Enqueue(b2); } } @@ -179,7 +201,8 @@ private static void AesCtrTransform(byte[] key, byte[] salt, Stream inputStream, } } - private static byte[] Transform(byte[] str, byte[] key, byte[] iv) { + private static byte[] Transform(byte[] str, byte[] key, byte[] iv) + { using var outputEncryptedStream = new MemoryStream(str); using var outputDecryptedStream = new MemoryStream(); AesCtrTransform(key, iv, outputEncryptedStream, outputDecryptedStream); @@ -187,4 +210,4 @@ private static byte[] Transform(byte[] str, byte[] key, byte[] iv) { outputDecryptedStream.Position = 0; return outputDecryptedStream.ToArray(); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Novelxo/NovelxoRuGetter.cs b/src/Core/Logic/Getters/Novelxo/NovelxoRuGetter.cs similarity index 67% rename from Core/Logic/Getters/Novelxo/NovelxoRuGetter.cs rename to src/Core/Logic/Getters/Novelxo/NovelxoRuGetter.cs index 22b6a3a8..6839cd46 100644 --- a/Core/Logic/Getters/Novelxo/NovelxoRuGetter.cs +++ b/src/Core/Logic/Getters/Novelxo/NovelxoRuGetter.cs @@ -1,8 +1,8 @@ -using System; using Core.Configs; -namespace Core.Logic.Getters.Novelxo; +namespace Core.Logic.Getters.Novelxo; -public class NovelxoRuGetter(BookGetterConfig config) : NovelxoGetterBase(config) { +public class NovelxoRuGetter(BookGetterConfig config) : NovelxoGetterBase(config) +{ protected override Uri SystemUrl => new("https://ru.novelxo.com"); -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/OnlineKnigiGetter.cs b/src/Core/Logic/Getters/OnlineKnigiGetter.cs similarity index 73% rename from Core/Logic/Getters/OnlineKnigiGetter.cs rename to src/Core/Logic/Getters/OnlineKnigiGetter.cs index cb40e662..27332446 100644 --- a/Core/Logic/Getters/OnlineKnigiGetter.cs +++ b/src/Core/Logic/Getters/OnlineKnigiGetter.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -10,40 +7,47 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class OnlineKnigiGetter(BookGetterConfig config) : GetterBase(config) { +public class OnlineKnigiGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://online-knigi.com.ua/"); - protected override string GetId(Uri url) => url.GetSegment(2); + protected override string GetId(Uri url) + => url.GetSegment(2); - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var bookId = GetId(url); url = SystemUrl.MakeRelativeUri($"/kniga/{bookId}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var title = doc.GetTextBySelector("h1"); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc), Chapters = await FillChapters(bookId, title), Title = title, Author = GetAuthor(doc, url), Annotation = doc.QuerySelector("div.book_description")?.InnerHtml, }; - + return book; } - private async Task> FillChapters(string bookId, string title) { - if (Config.Options.NoChapters) { + private async Task> FillChapters(string bookId, string title) + { + if (Config.Options.NoChapters) + { return []; } - + var chapter = new Chapter(); var sb = new StringBuilder(); var pages = await GetPages(bookId); - for (var i = 1; i <= pages; i++) { + for (var i = 1; i <= pages; i++) + { Config.Logger.LogInformation($"Получаю страницу {i}/{pages}"); var uri = SystemUrl.MakeRelativeUri($"/page/{bookId}?page={i}"); @@ -56,21 +60,27 @@ private async Task> FillChapters(string bookId, string titl chapter.Title = title; chapter.Content = chapterDoc.DocumentNode.InnerHtml; - return new[] { chapter }; + return new[] + { + chapter + }; } - private async Task GetPages(string bookId) { + private async Task GetPages(string bookId) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri($"/page/{bookId}")); - return int.Parse(doc.QuerySelector("li.last a").Attributes["href"].Value.AsUri().GetQueryParameter("page")); + return int.Parse(doc.QuerySelector("li.last a").Attributes["href"].Value.AsUri().GetQueryParameter("page")); } - private Task GetCover(HtmlDocument doc) { + private Task GetCover(HtmlDocument doc) + { var imagePath = doc.QuerySelector("meta[property=og:image]")?.Attributes["content"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(SystemUrl.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - - private static Author GetAuthor(HtmlDocument doc, Uri uri) { + + private static Author GetAuthor(HtmlDocument doc, Uri uri) + { var a = doc.QuerySelector("div[itemprop=author] a"); return new Author(a.GetText(), uri.MakeRelativeUri(a.Attributes["href"].Value)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/ProdamanGetter.cs b/src/Core/Logic/Getters/ProdamanGetter.cs similarity index 76% rename from Core/Logic/Getters/ProdamanGetter.cs rename to src/Core/Logic/Getters/ProdamanGetter.cs index 3b6d837a..bf777184 100644 --- a/Core/Logic/Getters/ProdamanGetter.cs +++ b/src/Core/Logic/Getters/ProdamanGetter.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -14,49 +9,57 @@ namespace Core.Logic.Getters; -public class ProdamanGetter : GetterBase { - public ProdamanGetter(BookGetterConfig config) : base(config) { +public class ProdamanGetter : GetterBase +{ + public ProdamanGetter(BookGetterConfig config) : base(config) + { InitMap(); } protected override Uri SystemUrl => new("https://prodaman.ru/"); - + private static readonly Dictionary Map = new(); - private static void Append(string str, int start) { - for (var i = 0; i < str.Length; i++) { + private static void Append(string str, int start) + { + for (var i = 0; i < str.Length; i++) + { Map[start + i] = str[i]; } } - + /// /// Авторизация в системе /// /// - public override async Task Authorize(){ - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } var doc = await Config.Client.PostHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri("/login"), GetAuthData()); - if (!string.IsNullOrWhiteSpace(doc.GetTextBySelector("p.error"))) { + if (!string.IsNullOrWhiteSpace(doc.GetTextBySelector("p.error"))) + { throw new Exception("Не удалось авторизоваться"); } } - - private FormUrlEncodedContent GetAuthData() { - var data = new Dictionary { - ["email"] = Config.Options.Login, - ["pass"] = Config.Options.Password, - ["remember"] = "on", + + private FormUrlEncodedContent GetAuthData() + { + var data = new Dictionary + { + ["email"] = Config.Options.Login, ["pass"] = Config.Options.Password, ["remember"] = "on", }; return new FormUrlEncodedContent(data); } - + // MAAAAAAGIC!!! - private static void InitMap() { + private static void InitMap() + { Append("ЕКЦДНСИПТЬЧЖФ", 0x02CB); Append("ЗЭЯГЛЙОЮУХРМЩ", 0x02DE); Append("е", 0x02EC); @@ -117,12 +120,14 @@ private static void InitMap() { Append("нх,ьтзщядвусцшкмеэр.бл", 0x25D0); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri(url.AbsolutePath); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var title = doc.GetTextBySelector("h1"); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapter(url, title), Title = title, @@ -134,114 +139,142 @@ public override async Task Get(Uri url) { return book; } - private static Author GetAuthor(HtmlDocument doc, Uri url) { + private static Author GetAuthor(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("a[data-widget-feisovet-author]"); - if (a == default) { + if (a == null) + { return new Author("Prodaman"); } return new Author(a.GetText(), url.MakeRelativeUri(a.Attributes["href"].Value)); } - private static Seria GetSeria(HtmlDocument doc, Uri url) { + private static Seria GetSeria(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("p.blog-info a[href*=/series/]"); - if (a != default) { - return new Seria { - Name = a.GetText(), - Url = url.MakeRelativeUri(a.Attributes["href"].Value) + if (a != null) + { + return new Seria + { + Name = a.GetText(), Url = url.MakeRelativeUri(a.Attributes["href"].Value) }; } - return default; + return null; } - private async Task GetPages(Uri url) { + private async Task GetPages(Uri url) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(url.AppendQueryParameter("nav", "ok")); var pages = doc.QuerySelectorAll("div.pageList a") .Where(a => int.TryParse(a.InnerText, out _)) .Select(a => int.Parse(a.InnerText)) .ToList(); - + return pages.Count > 0 ? pages.Max() : 1; } - - private static bool IsHeaderStart(IEnumerable nodes) { + + private static bool IsHeaderStart(IEnumerable nodes) + { var firstNode = nodes.First(); return firstNode.Name == "h3" && !string.IsNullOrWhiteSpace(firstNode.InnerText); } - - private static Chapter CreateChapter(string title) { - return new Chapter { + + private static Chapter CreateChapter(string title) + { + return new Chapter + { Title = title }; } - - private async Task AddChapter(ICollection chapters, Chapter chapter, StringBuilder text, Uri url) { - if (chapter == null) { + + private async Task AddChapter(ICollection chapters, Chapter chapter, StringBuilder text, Uri url) + { + if (chapter == null) + { return; } - + var chapterDoc = text.ToString().AsHtmlDoc(); chapter.Images = await GetImages(chapterDoc, url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; chapters.Add(chapter); } - private static string Decode(string encode) { + private static string Decode(string encode) + { return encode.Aggregate(new StringBuilder(), (sb, c) => sb.Append(Map.GetValueOrDefault(c, c))).ToString(); } - private async Task> FillChapter(Uri url, string title) { + private async Task> FillChapter(Uri url, string title) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - + Chapter chapter = null; var text = new StringBuilder(); var pages = await GetPages(url); var firstBr = true; - for (var i = 1; i <= pages; i++) { + for (var i = 1; i <= pages; i++) + { Config.Logger.LogInformation($"Получаю страницу {i}/{pages}"); var appendSegment = url.AppendQueryParameter("page", i); var page = await Config.Client.GetHtmlDocWithTriesAsync(appendSegment); var content = page.QuerySelector("div.blog-text").RemoveNodes("a[href*=snoska], div[id*=snoska]"); var nodes = content.ChildNodes; - if (i == 1 && !IsHeaderStart(nodes)) { + if (i == 1 && !IsHeaderStart(nodes)) + { chapter = CreateChapter(title); } - foreach (var node in nodes) { - if (node.Name != "h3" || (node.Name == "h3" && node.GetText() == "***")) { - if (node.Name == "br") { - if (firstBr) { + foreach (var node in nodes) + { + if (node.Name != "h3" || (node.Name == "h3" && node.GetText() == "***")) + { + if (node.Name == "br") + { + if (firstBr) + { firstBr = false; text.Append("

"); - } else { + } + else + { text.Append("

"); } - + continue; } - - if (node.Name == "img" && node.Attributes["src"] != null) { + + if (node.Name == "img" && node.Attributes["src"] != null) + { text.Append($""); - } else if (!string.IsNullOrWhiteSpace(node.InnerHtml)) { + } + else if (!string.IsNullOrWhiteSpace(node.InnerHtml)) + { var pText = Decode(node.InnerHtml.HtmlDecode()); - - if (node.InnerHtml.StartsWith(" ")) { + + if (node.InnerHtml.StartsWith(" ")) + { pText = " " + pText; } - if (node.InnerHtml.EndsWith(" ")) { + if (node.InnerHtml.EndsWith(" ")) + { pText += " "; } - + text.Append(pText.CoverTag(node.Name == "#text" ? string.Empty : node.Name)); } - } else { + } + else + { text.Append("

"); await AddChapter(result, chapter, text, url); text.Clear(); @@ -249,14 +282,15 @@ private async Task> FillChapter(Uri url, string title) { } } } - + text.Append("

"); await AddChapter(result, chapter ?? CreateChapter(title), text, url); return result; } - private Task GetCover(HtmlDocument doc, Uri url) { + private Task GetCover(HtmlDocument doc, Uri url) + { var imagePath = doc.QuerySelector("div[itemprop=aggregateRating] img[itemprop=image]")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(url.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/RanobeGetter.cs b/src/Core/Logic/Getters/RanobeGetter.cs similarity index 83% rename from Core/Logic/Getters/RanobeGetter.cs rename to src/Core/Logic/Getters/RanobeGetter.cs index 6ae5ec4b..5d07e4b7 100644 --- a/Core/Logic/Getters/RanobeGetter.cs +++ b/src/Core/Logic/Getters/RanobeGetter.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Text.Json; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -12,37 +8,44 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class RanobeGetter(BookGetterConfig config) : GetterBase(config) { +public class RanobeGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://ранобэ.рф/"); - - protected override string GetId(Uri url) => url.Segments.Length == 2 ? base.GetId(url) : url.GetSegment(1); - public override async Task Get(Uri url) { + protected override string GetId(Uri url) + => url.Segments.Length == 2 ? base.GetId(url) : url.GetSegment(1); + + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri(GetId(url)); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var ranobeBook = GetNextData(doc, "book"); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(ranobeBook, url), Chapters = await FillChapters(ranobeBook, url), Title = ranobeBook.Title, Author = new Author(string.IsNullOrWhiteSpace(ranobeBook.Author) ? "Ranobe" : ranobeBook.Author) }; - + return book; } - private async Task> FillChapters(RanobeBook ranobeBook, Uri url) { + private async Task> FillChapters(RanobeBook ranobeBook, Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } ranobeBook.Chapters.Reverse(); - foreach (var ranobeChapter in SliceToc(ranobeBook.Chapters, c => c.Title)) { + foreach (var ranobeChapter in SliceToc(ranobeBook.Chapters, c => c.Title)) + { Config.Logger.LogInformation($"Загружаю главу {ranobeChapter.Title.CoverQuotes()}"); var chapter = new Chapter(); var doc = await GetChapter(url, ranobeChapter.Url); @@ -56,12 +59,14 @@ private async Task> FillChapters(RanobeBook ranobeBook, Uri return result; } - private async Task GetChapter(Uri mainUrl, string url) { + private async Task GetChapter(Uri mainUrl, string url) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(mainUrl.MakeRelativeUri(url)); return GetNextData(doc, "chapter").Content.Text.AsHtmlDoc(); } - private static T GetNextData(HtmlDocument doc, string node) { + private static T GetNextData(HtmlDocument doc, string node) + { var json = doc.QuerySelector("#__NEXT_DATA__").InnerText; return JsonDocument.Parse(json) .RootElement.GetProperty("props") @@ -70,9 +75,10 @@ private static T GetNextData(HtmlDocument doc, string node) { .GetRawText() .Deserialize(); } - - private Task GetCover(RanobeBook book, Uri bookUri) { + + private Task GetCover(RanobeBook book, Uri bookUri) + { var imagePath = book.Image?.Url ?? book.Images.MaxBy(t => t.Height)?.Url; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/RanobeHubGetter.cs b/src/Core/Logic/Getters/RanobeHubGetter.cs similarity index 75% rename from Core/Logic/Getters/RanobeHubGetter.cs rename to src/Core/Logic/Getters/RanobeHubGetter.cs index 4372df6c..5255c1fa 100644 --- a/Core/Logic/Getters/RanobeHubGetter.cs +++ b/src/Core/Logic/Getters/RanobeHubGetter.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Json; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -12,35 +8,38 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class RanobeHubGetter(BookGetterConfig config) : GetterBase(config) { +public class RanobeHubGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://ranobehub.org/"); - - public override async Task Get(Uri url) { + + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri($"/ranobe/{GetId(url)}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { - Cover = await GetCover(doc, url), - Chapters = await FillChapters(doc, url), - Title = doc.GetTextBySelector("h1.header"), - Author = new Author("RanobeHub") + var book = new Book(url) + { + Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("h1.header"), Author = new Author("RanobeHub") }; - + return book; } - private async Task> FillChapters(HtmlDocument doc, Uri url) { + private async Task> FillChapters(HtmlDocument doc, Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var ranobeChapter in await GetToc(doc)) { + foreach (var ranobeChapter in await GetToc(doc)) + { Config.Logger.LogInformation($"Загружаю главу {ranobeChapter.Name}"); var chapter = new Chapter(); - + var chapterDoc = await GetChapter(ranobeChapter.Url); chapter.Images = await GetImages(chapterDoc, url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; @@ -52,40 +51,49 @@ private async Task> FillChapters(HtmlDocument doc, Uri url) return result; } - private async Task GetChapter(string url) { + private async Task GetChapter(string url) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(url.AsUri()); - while (doc.QuerySelector("div[data-callback=correctCaptcha]") != null) { + while (doc.QuerySelector("div[data-callback=correctCaptcha]") != null) + { Config.Logger.LogInformation($"Обнаружена капча. Перейдите по ссылке {url}, введите капчу и нажмите Enter..."); Console.Read(); doc = await Config.Client.GetHtmlDocWithTriesAsync(url.AsUri()); } - + var result = doc.QuerySelector("div.container[data-container]").RemoveNodes("div.title-wrapper, div.ads-desktop, div.tablet, div.chapter-hoticons").InnerHtml.AsHtmlDoc(); - - foreach (var img in result.QuerySelectorAll("img")) { + + foreach (var img in result.QuerySelectorAll("img")) + { var id = img.Attributes["data-media-id"]?.Value; - if (string.IsNullOrWhiteSpace(id)) { + if (string.IsNullOrWhiteSpace(id)) + { continue; } - if (img.Attributes.Contains("src")) { + if (img.Attributes.Contains("src")) + { img.Attributes["src"].Value = $"/api/media/{id}"; - } else { + } + else + { img.Attributes.Add("src", $"/api/media/{id}"); } } - + return result; } - private async Task> GetToc(HtmlDocument doc) { + private async Task> GetToc(HtmlDocument doc) + { var internalId = doc.QuerySelector("html[data-id]").Attributes["data-id"].Value; var response = await Config.Client.GetFromJsonAsync(SystemUrl.MakeRelativeUri($"/api/ranobe/{internalId}/contents")); return SliceToc(response?.Volumes.SelectMany(t => t.Chapters).ToList(), c => c.Name); } - - private Task GetCover(HtmlDocument doc, Uri bookUri) { + + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("div.poster-slider img")?.Attributes["data-src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/RanobeNovelsGetter.cs b/src/Core/Logic/Getters/RanobeNovelsGetter.cs similarity index 73% rename from Core/Logic/Getters/RanobeNovelsGetter.cs rename to src/Core/Logic/Getters/RanobeNovelsGetter.cs index 1ad4324f..16975c18 100644 --- a/Core/Logic/Getters/RanobeNovelsGetter.cs +++ b/src/Core/Logic/Getters/RanobeNovelsGetter.cs @@ -1,11 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Net.Http; using System.Net.Http.Json; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -15,39 +10,43 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class RanobeNovelsGetter(BookGetterConfig config) : GetterBase(config) { +public class RanobeNovelsGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://ranobe-novels.ru/"); - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var bookId = GetId(url); url = SystemUrl.MakeRelativeUri($"/ranobe/{bookId}/"); var doc = await GetSafety(url); - var book = new Book(url) { - Cover = await GetCover(doc, url), - Chapters = await FillChapters(doc, url), - Title = doc.GetTextBySelector("h1"), - Author = new Author("ranobe-novels") + var book = new Book(url) + { + Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("h1"), Author = new Author("ranobe-novels") }; - + return book; } - - private async Task> FillChapters(HtmlDocument doc, Uri url) { + + private async Task> FillChapters(HtmlDocument doc, Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } var toc = await GetToc(doc); - if (toc.Count == 0) { + if (toc.Count == 0) + { doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri($"/{GetId(url)}")); toc = await GetToc(doc); } - - foreach (var ranobeChapter in toc) { + + foreach (var ranobeChapter in toc) + { Config.Logger.LogInformation($"Загружаю главу {ranobeChapter.Title.CoverQuotes()}"); var chapter = new Chapter(); var chapterDoc = await GetChapter(ranobeChapter.Url); @@ -61,21 +60,25 @@ private async Task> FillChapters(HtmlDocument doc, Uri url) return result; } - private async Task GetChapter(Uri url) { + private async Task GetChapter(Uri url) + { var doc = await GetSafety(url); return doc.QuerySelector("div.js-full-content").RemoveNodes("> :not(p)").InnerHtml.AsHtmlDoc(); } - private async Task> GetToc(HtmlDocument doc) { + private async Task> GetToc(HtmlDocument doc) + { var catId = Regex.Match(doc.ParsedText, "data-cat=\"(?\\d+)\"").Groups["catId"].Value; var result = new List(); - - foreach (var span in doc.QuerySelectorAll("ul.myUL span.caret[data-id]")) { + + foreach (var span in doc.QuerySelectorAll("ul.myUL span.caret[data-id]")) + { var offset = (int.Parse(span.Attributes["data-id"].Value) - 1) * 100; - var payload = new FormUrlEncodedContent(new Dictionary { - ["cat_id"] = catId, - ["offset"] = offset.ToString() - }); + var payload = new FormUrlEncodedContent( + new Dictionary + { + ["cat_id"] = catId, ["offset"] = offset.ToString() + }); var post = await Config.Client.PostWithTriesAsync(SystemUrl.MakeRelativeUri("/wp-content/themes/ranobe-novels/template-parts/post/menu-query.php"), payload); var toc = await post.Content.ReadFromJsonAsync(); @@ -85,22 +88,25 @@ private async Task> GetToc(HtmlDocument doc) { return SliceToc(result, c => c.Title).ToList(); } - private Task GetCover(HtmlDocument doc, Uri uri) { + private Task GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("meta[property=og:image]")?.Attributes["content"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - private async Task GetSafety(Uri url) { + private async Task GetSafety(Uri url) + { var response = await Config.Client.GetWithTriesAsync(url); await Task.Delay(TimeSpan.FromSeconds(1)); - - while (response == default || response.StatusCode == HttpStatusCode.ServiceUnavailable) { + + while (response == null || response.StatusCode == HttpStatusCode.ServiceUnavailable) + { Config.Logger.LogInformation("Получен бан от системы. Жду..."); var errorTimeout = TimeSpan.FromSeconds(30); response = await Config.Client.GetWithTriesAsync(url, errorTimeout); await Task.Delay(errorTimeout); } - + return await response.Content.ReadAsStreamAsync().ContinueWith(t => t.Result.AsHtmlDoc()); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/RanobeOvh/MangaOvhGetter.cs b/src/Core/Logic/Getters/RanobeOvh/MangaOvhGetter.cs similarity index 76% rename from Core/Logic/Getters/RanobeOvh/MangaOvhGetter.cs rename to src/Core/Logic/Getters/RanobeOvh/MangaOvhGetter.cs index a35d2ced..316e49b2 100644 --- a/Core/Logic/Getters/RanobeOvh/MangaOvhGetter.cs +++ b/src/Core/Logic/Getters/RanobeOvh/MangaOvhGetter.cs @@ -1,25 +1,26 @@ -using System; using System.Net.Http.Json; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.RanobeOvh; using HtmlAgilityPack; -namespace Core.Logic.Getters.RanobeOvh; +namespace Core.Logic.Getters.RanobeOvh; -public class MangaOvhGetter(BookGetterConfig config) : RanobeOvhGetterBase(config) { +public class MangaOvhGetter(BookGetterConfig config) : RanobeOvhGetterBase(config) +{ protected override Uri SystemUrl => new("https://manga.ovh/"); - protected override async Task GetChapter(RanobeOvhChapterShort ranobeOvhChapterFull) { + protected override async Task GetChapter(RanobeOvhChapterShort ranobeOvhChapterFull) + { var data = await Config.Client.GetFromJsonAsync($"https://api.{SystemUrl.Host}/chapter/{ranobeOvhChapterFull.Id}"); var sb = new StringBuilder(); - foreach (var page in data.Pages) { + foreach (var page in data.Pages) + { sb.Append($""); } return sb.AsHtmlDoc(); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/RanobeOvh/NovelOvhGetter.cs b/src/Core/Logic/Getters/RanobeOvh/NovelOvhGetter.cs similarity index 64% rename from Core/Logic/Getters/RanobeOvh/NovelOvhGetter.cs rename to src/Core/Logic/Getters/RanobeOvh/NovelOvhGetter.cs index 1253d37e..d12d026b 100644 --- a/Core/Logic/Getters/RanobeOvh/NovelOvhGetter.cs +++ b/src/Core/Logic/Getters/RanobeOvh/NovelOvhGetter.cs @@ -1,62 +1,90 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Json; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.RanobeOvh; using HtmlAgilityPack; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.RanobeOvh; +namespace Core.Logic.Getters.RanobeOvh; -public class NovelOvhGetter(BookGetterConfig config) : RanobeOvhGetterBase(config) { +public class NovelOvhGetter(BookGetterConfig config) : RanobeOvhGetterBase(config) +{ protected override Uri SystemUrl => new("https://novel.ovh/"); - - private static readonly Dictionary RecursiveTag = new() { - { "paragraph", "p" }, - { "orderedList", "ol" }, - { "listItem", "li" }, - { "blockquote", "blockquote" }, + + private static readonly Dictionary RecursiveTag = new() + { + { + "paragraph", "p" + }, + { + "orderedList", "ol" + }, + { + "listItem", "li" + }, + { + "blockquote", "blockquote" + }, }; - - private static readonly Dictionary InlineTag = new() { - { "horizontalRule", "
" }, - { "hardBreak", "
" }, + + private static readonly Dictionary InlineTag = new() + { + { + "horizontalRule", "
" + }, + { + "hardBreak", "
" + }, }; - - private static readonly Dictionary MarkTag = new() { - { "italic", "i" }, - { "bold", "b" }, - { "underline", "u" }, + + private static readonly Dictionary MarkTag = new() + { + { + "italic", "i" + }, + { + "bold", "b" + }, + { + "underline", "u" + }, }; - private StringBuilder AsHtml(RanobeOvhChapterFull chapterFullResponse, IEnumerable contents) { + private StringBuilder AsHtml(RanobeOvhChapterFull chapterFullResponse, IEnumerable contents) + { var sb = new StringBuilder(); - foreach (var content in contents) { - if (RecursiveTag.TryGetValue(content.Type, out var tag)) { - sb.Append(AsHtml(chapterFullResponse, content.Content).ToString().CoverTag(tag)); + foreach (var content in contents) + { + if (RecursiveTag.TryGetValue(content.Type, out var tag)) + { + sb.Append(AsHtml(chapterFullResponse, content.Content).ToString().CoverTag(tag)); continue; } - - if (InlineTag.TryGetValue(content.Type, out tag)) { + + if (InlineTag.TryGetValue(content.Type, out tag)) + { sb.Append(tag); continue; } - switch (content.Type) { - case "text": { + switch (content.Type) + { + case "text": + { var text = content.Text.HtmlEncode(); - foreach (var mark in content.Marks) { - if (MarkTag.TryGetValue(mark.Type, out tag)) { + foreach (var mark in content.Marks) + { + if (MarkTag.TryGetValue(mark.Type, out tag)) + { text = text.CoverTag(tag); - } else { + } + else + { Config.Logger.LogInformation($"Неизвестый тип форматирования {mark.Type}"); } } @@ -64,18 +92,22 @@ private StringBuilder AsHtml(RanobeOvhChapterFull chapterFullResponse, IEnumerab sb.Append(text); continue; } - case "image": { - if (content.Attrs.TryGetValue("pages", out var pages)) { - foreach (var imageId in pages.Deserialize()) { + case "image": + { + if (content.Attrs.TryGetValue("pages", out var pages)) + { + foreach (var imageId in pages.Deserialize()) + { var attachment = chapterFullResponse.Pages.FirstOrDefault(a => a.Id == imageId); - if (attachment == default) { + if (attachment == null) + { continue; } - + sb.Append($""); } } - + continue; } default: @@ -86,13 +118,15 @@ private StringBuilder AsHtml(RanobeOvhChapterFull chapterFullResponse, IEnumerab return sb; } - - protected override async Task GetChapter(RanobeOvhChapterShort ranobeOvhChapterFull) { + + protected override async Task GetChapter(RanobeOvhChapterShort ranobeOvhChapterFull) + { var data = await Config.Client.GetFromJsonAsync($"https://api.{SystemUrl.Host}/v2/chapters/{ranobeOvhChapterFull.Id}"); - return data.Content switch { + return data.Content switch + { JsonValue e => e.GetValue().AsHtmlDoc(), JsonObject o => AsHtml(data, o.Deserialize().Content).AsHtmlDoc(), _ => throw new Exception("Неизвестный тип") }; } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/RanobeOvh/RanobeOvhGetterBase.cs b/src/Core/Logic/Getters/RanobeOvh/RanobeOvhGetterBase.cs similarity index 80% rename from Core/Logic/Getters/RanobeOvh/RanobeOvhGetterBase.cs rename to src/Core/Logic/Getters/RanobeOvh/RanobeOvhGetterBase.cs index 862e09db..2bc25691 100644 --- a/Core/Logic/Getters/RanobeOvh/RanobeOvhGetterBase.cs +++ b/src/Core/Logic/Getters/RanobeOvh/RanobeOvhGetterBase.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; using System.Text.Json; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -13,24 +8,28 @@ using HtmlAgilityPack; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.RanobeOvh; +namespace Core.Logic.Getters.RanobeOvh; -public abstract class RanobeOvhGetterBase(BookGetterConfig config) : GetterBase(config) { - protected override string GetId(Uri url) { +public abstract class RanobeOvhGetterBase(BookGetterConfig config) : GetterBase(config) +{ + protected override string GetId(Uri url) + { return url.GetSegment(2); } protected abstract Task GetChapter(RanobeOvhChapterShort ranobeOvhChapterFull); - - public override async Task Get(Uri url) { + + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri($"/content/{GetId(url)}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - + var manga = GetNextData(doc, "book"); var chapters = GetNextData(doc, "chapters"); var branch = GetBranch(doc); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(manga, url), Chapters = await FillChapters(branch, chapters, url), Title = manga.Name.Ru, @@ -40,20 +39,24 @@ public override async Task Get(Uri url) { return book; } - - protected override HttpRequestMessage GetImageRequestMessage(Uri uri) { + + protected override HttpRequestMessage GetImageRequestMessage(Uri uri) + { var message = base.GetImageRequestMessage(uri); message.Headers.Referrer = SystemUrl; return message; } - private async Task> FillChapters(RanobeOvhBranch branch, RanobeOvhChapterShort[] chapters, Uri url) { + private async Task> FillChapters(RanobeOvhBranch branch, RanobeOvhChapterShort[] chapters, Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var ranobeOvhChapter in GetToc(branch, chapters)) { + foreach (var ranobeOvhChapter in GetToc(branch, chapters)) + { var chapter = new Chapter(); Config.Logger.LogInformation($"Загружаю главу {ranobeOvhChapter.FullName.CoverQuotes()}"); @@ -68,7 +71,8 @@ private async Task> FillChapters(RanobeOvhBranch branch, Ra return result; } - private T GetNextData(HtmlDocument doc, string node) { + private T GetNextData(HtmlDocument doc, string node) + { var json = Regex.Match(doc.ParsedText, "__remixContext = (?.*?);", RegexOptions.Singleline).Groups["data"].Value; return JsonDocument.Parse(json) .RootElement.GetProperty("state") @@ -79,25 +83,30 @@ private T GetNextData(HtmlDocument doc, string node) { .Deserialize(); } - private IEnumerable GetToc(RanobeOvhBranch branch, RanobeOvhChapterShort[] chapters) { + private IEnumerable GetToc(RanobeOvhBranch branch, RanobeOvhChapterShort[] chapters) + { return SliceToc(chapters.Where(c => c.BranchId == branch.Id).Reverse().ToList(), c => c.FullName); } - private RanobeOvhBranch GetBranch(HtmlDocument doc) { + private RanobeOvhBranch GetBranch(HtmlDocument doc) + { var branches = GetNextData(doc, "branches"); return branches.MaxBy(c => c.ChaptersCount); } - private Author GetAuthor(RanobeOvhBranch branch) { - if (branch.Translators == null) { + private Author GetAuthor(RanobeOvhBranch branch) + { + if (branch.Translators == null) + { return new Author("RanobeOvh"); } - + var translator = branch.Translators[0]; return new Author(translator.Name, SystemUrl.MakeRelativeUri($"/translator/{translator.Slug}")); } - private Task GetCover(RanobeOvhManga manga, Uri uri) { + private Task GetCover(RanobeOvhManga manga, Uri uri) + { return !string.IsNullOrWhiteSpace(manga.Poster) ? SaveImage(uri.MakeRelativeUri(manga.Poster)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/RanobesComGetter.cs b/src/Core/Logic/Getters/RanobesComGetter.cs similarity index 74% rename from Core/Logic/Getters/RanobesComGetter.cs rename to src/Core/Logic/Getters/RanobesComGetter.cs index 233a883d..f533764e 100644 --- a/Core/Logic/Getters/RanobesComGetter.cs +++ b/src/Core/Logic/Getters/RanobesComGetter.cs @@ -1,12 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Net.Http; using System.Net.Http.Json; using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -16,32 +11,37 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class RanobesComGetter(BookGetterConfig config) : GetterBase(config) { +public class RanobesComGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://ranobes.com/"); - protected override string GetId(Uri url) => base.GetId(url).Split(".")[0]; + protected override string GetId(Uri url) + => base.GetId(url).Split(".")[0]; - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = await GetMainUrl(url); url = SystemUrl.MakeRelativeUri($"/ranobe/{GetId(url)}.html"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); await Antibot(doc, url); - doc = await Config.Client.GetHtmlDocWithTriesAsync(url); + doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.QuerySelector("h1.title").FirstChild.InnerText.Trim().HtmlDecode(), Author = new Author(doc.GetTextBySelector("span[itemprop=creator]") ?? "Ranobes"), Annotation = doc.QuerySelector("div[itemprop=description]")?.RemoveNodes("style")?.InnerHtml }; - + return book; } - private async Task Antibot(HtmlDocument doc, Uri referrer) { + private async Task Antibot(HtmlDocument doc, Uri referrer) + { var h1 = Regex.Match(doc.ParsedText, "var h1 = \'(?.*?)\'").Groups["id"].Value; var h2 = Regex.Match(doc.ParsedText, "var h2 = \'(?.*?)\'").Groups["id"].Value; var date = Regex.Match(doc.ParsedText, "var date = \'(?.*?)\'").Groups["id"].Value; @@ -51,28 +51,67 @@ private async Task Antibot(HtmlDocument doc, Uri referrer) { var v = Regex.Match(doc.ParsedText, "var v = \'(?.*?)\'").Groups["id"].Value; var antibot = Regex.Match(doc.ParsedText, "antibot_(?.*?)=").Groups["id"].Value; - var data = new Dictionary { - { "hdc", "0" }, - { "scheme", "https" }, - { "a", "0" }, - { "date", date }, - { "country", "RU" }, - { "h1", h1 }, - { "h2", h2 }, - { "ip", ip }, - { "v", v }, - { "cid", cid }, - { "ptr", ptr }, - { "w", "2560" }, - { "h", "1440" }, - { "cw", "2560" }, - { "ch", "770" }, - { "co", "24" }, - { "pi", "24" }, - { "ref", "ranobes.com" }, - { "xxx", string.Empty }, + var data = new Dictionary + { + { + "hdc", "0" + }, + { + "scheme", "https" + }, + { + "a", "0" + }, + { + "date", date + }, + { + "country", "RU" + }, + { + "h1", h1 + }, + { + "h2", h2 + }, + { + "ip", ip + }, + { + "v", v + }, + { + "cid", cid + }, + { + "ptr", ptr + }, + { + "w", "2560" + }, + { + "h", "1440" + }, + { + "cw", "2560" + }, + { + "ch", "770" + }, + { + "co", "24" + }, + { + "pi", "24" + }, + { + "ref", "ranobes.com" + }, + { + "xxx", string.Empty + }, }; - + Config.Client.DefaultRequestHeaders.Add("Referer", referrer.ToString()); var post = await Config.Client.PostAsync(SystemUrl.MakeRelativeUri("antibot8/ab.php"), new FormUrlEncodedContent(data)); var cookie = await post.Content.ReadFromJsonAsync(); @@ -80,8 +119,10 @@ private async Task Antibot(HtmlDocument doc, Uri referrer) { Config.Client.DefaultRequestHeaders.Remove("Referer"); } - private async Task GetMainUrl(Uri url) { - if (url.GetSegment(1) == "chapters" || !url.Segments.Last().EndsWith(".html")) { + private async Task GetMainUrl(Uri url) + { + if (url.GetSegment(1) == "chapters" || !url.Segments.Last().EndsWith(".html")) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri(url.AbsolutePath)); return url.MakeRelativeUri(doc.QuerySelector("a[rel=up]").Attributes["href"].Value); } @@ -89,13 +130,16 @@ private async Task GetMainUrl(Uri url) { return url; } - private async Task> FillChapters(HtmlDocument doc, Uri url) { + private async Task> FillChapters(HtmlDocument doc, Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var ranobeChapter in await GetToc(GetTocLink(doc, url))) { + foreach (var ranobeChapter in await GetToc(GetTocLink(doc, url))) + { Config.Logger.LogInformation($"Загружаю главу {ranobeChapter.Title.CoverQuotes()}"); var chapter = new Chapter(); var chapterDoc = await GetChapter(ranobeChapter); @@ -109,57 +153,68 @@ private async Task> FillChapters(HtmlDocument doc, Uri url) return result; } - private async Task GetChapter(UrlChapter chapter) { + private async Task GetChapter(UrlChapter chapter) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(chapter.Url); var sb = new StringBuilder(); - foreach (var node in doc.QuerySelectorAll("#arrticle > :not(.splitnewsnavigation)")) { + foreach (var node in doc.QuerySelectorAll("#arrticle > :not(.splitnewsnavigation)")) + { var tag = node.Name == "#text" ? "p" : node.Name; - if (tag == "img") { + if (tag == "img") + { sb.Append(node.OuterHtml.Trim().CoverTag(tag)); - } else { - if (node.InnerHtml?.Contains("window.yaContextCb") == false) { + } + else + { + if (node.InnerHtml?.Contains("window.yaContextCb") == false) + { sb.Append(node.InnerHtml.CoverTag(tag)); } } } - + return sb.AsHtmlDoc().RemoveNodes(t => t.Name is "script" or "br" || t.Id?.Contains("yandex_rtb") == true); } - private Task GetCover(HtmlDocument doc, Uri bookUri) { + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("div.poster img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - private Uri GetTocLink(HtmlDocument doc, Uri uri) { + private Uri GetTocLink(HtmlDocument doc, Uri uri) + { var relativeUri = doc.QuerySelector("div.r-fullstory-btns a[title~=оглавление]").Attributes["href"].Value.HtmlDecode(); - if (!relativeUri.Contains("chapters")) { + if (!relativeUri.Contains("chapters")) + { relativeUri = $"/chapters/{string.Join("-", GetId(uri).Split(".")[0].Split("-").Skip(1))}"; } - + return uri.MakeRelativeUri(relativeUri.AsUri().AbsolutePath.Trim('/')); } - - private async Task> GetToc(Uri tocUri) { + + private async Task> GetToc(Uri tocUri) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(tocUri); var lastA = doc.QuerySelector("div.pages a:last-child")?.InnerText; var pages = string.IsNullOrWhiteSpace(lastA) ? 1 : int.Parse(lastA); - + Config.Logger.LogInformation("Получаю оглавление"); var result = new List(); - for (var i = 1; i <= pages; i++) { + for (var i = 1; i <= pages; i++) + { doc = await Config.Client.GetHtmlDocWithTriesAsync(tocUri.AppendSegment($"/page/{i}")); var chapters = doc .QuerySelectorAll("#dle-content > .cat_block.cat_line a[title]") .Select(a => new UrlChapter(a.Attributes["href"].Value.AsUri(), string.IsNullOrWhiteSpace(a.Attributes["title"].Value) ? "Без названия" : a.Attributes["title"].Value)) .ToList(); - + result.AddRange(chapters); } - + Config.Logger.LogInformation($"Получено {result.Count} глав"); result.Reverse(); return SliceToc(result, c => c.Title); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/ReadMangaGetter.cs b/src/Core/Logic/Getters/ReadMangaGetter.cs similarity index 78% rename from Core/Logic/Getters/ReadMangaGetter.cs rename to src/Core/Logic/Getters/ReadMangaGetter.cs index 0e0223a5..bd1522c4 100644 --- a/Core/Logic/Getters/ReadMangaGetter.cs +++ b/src/Core/Logic/Getters/ReadMangaGetter.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -13,85 +9,103 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class ReadMangaGetter(BookGetterConfig config) : GetterBase(config) { +public class ReadMangaGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://readmanga.live/"); - protected override string GetId(Uri url) => url.GetSegment(1); - public override async Task Get(Uri url) { + protected override string GetId(Uri url) + => url.GetSegment(1); + + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri(GetId(url)); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("span.name"), Author = new Author("ReadManga"), Annotation = doc.QuerySelector("div.manga-description").InnerHtml }; - + return book; } - private async Task> FillChapters(HtmlDocument doc, Uri url) { + private async Task> FillChapters(HtmlDocument doc, Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - - foreach (var urlChapter in GetToc(doc, url)) { + + foreach (var urlChapter in GetToc(doc, url)) + { Config.Logger.LogInformation($"Загружаю главу {urlChapter.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = urlChapter.Title }; var chapterDoc = await GetChapter(urlChapter); - if (chapterDoc != default) { + if (chapterDoc != null) + { chapter.Images = await GetImages(chapterDoc, url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } - + result.Add(chapter); } return result; } - private IEnumerable GetToc(HtmlDocument doc, Uri url) { + private IEnumerable GetToc(HtmlDocument doc, Uri url) + { var result = doc .QuerySelectorAll("td.item-title a.chapter-link") .Select(a => new UrlChapter(url.MakeRelativeUri(a.Attributes["href"].Value), a.GetText().ReplaceNewLine())) .Reverse() .ToList(); + return SliceToc(result, c => c.Title); } - private async Task GetChapter(UrlChapter urlChapter) { + private async Task GetChapter(UrlChapter urlChapter) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(urlChapter.Url.AppendQueryParameter("mtr", "1")); - if (doc.QuerySelector("div.buy-button") != default) { - return default; + if (doc.QuerySelector("div.buy-button") != null) + { + return null; } - + var json = Regex.Match(doc.ParsedText, @"readerInit(.*?)(?\[\[(.*?)]]),").Groups["data"].Value.Replace("'", "\"").Deserialize>(); var sb = new StringBuilder(); - - foreach (var elem in json) { + + foreach (var elem in json) + { var imageUri = elem[0].GetString().AsUri().MakeRelativeUri(elem[2].GetString()); // Это не костыль. На сайте также - if (imageUri.Host == "one-way.work") { + if (imageUri.Host == "one-way.work") + { imageUri = imageUri.MakeRelativeUri(imageUri.AbsolutePath); } + sb.Append($""); } - + return sb.AsHtmlDoc(); } - private Task GetCover(HtmlDocument doc, Uri bookUri) { + private Task GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("div.picture-fotorama img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/ReadliGetter.cs b/src/Core/Logic/Getters/ReadliGetter.cs similarity index 75% rename from Core/Logic/Getters/ReadliGetter.cs rename to src/Core/Logic/Getters/ReadliGetter.cs index 7cb0fe65..df3932df 100644 --- a/Core/Logic/Getters/ReadliGetter.cs +++ b/src/Core/Logic/Getters/ReadliGetter.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -11,20 +7,23 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class ReadliGetter(BookGetterConfig config) : GetterBase(config) { +public class ReadliGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://readli.net"); - + // cloudflare :( private readonly Uri _host = new("https://213.227.140.81/"); - public override async Task Init() { + public override async Task Init() + { await base.Init(); Config.Client.DefaultRequestHeaders.Add("Host", SystemUrl.Host); } - - public override async Task Get(Uri url) { + + public override async Task Get(Uri url) + { url = await GetMainUrl(url); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); @@ -32,9 +31,10 @@ public override async Task Get(Uri url) { var imageDiv = doc.QuerySelector("div.book-image"); var href = url.MakeRelativeUri(imageDiv.QuerySelector("a").Attributes["href"].Value); var bookId = GetBookId(href); - + var title = doc.GetTextBySelector("h1.main-info__title"); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(imageDiv, url), Chapters = await FillChapters(bookId, pages, title), Title = title, @@ -43,45 +43,50 @@ public override async Task Get(Uri url) { Seria = GetSeria(doc, url) }; - return book; + return book; } - - private static Author GetAuthor(HtmlDocument doc, Uri url) { + + private static Author GetAuthor(HtmlDocument doc, Uri url) + { var author = doc.QuerySelector("a.main-info__link"); return new Author(author.InnerText.HtmlDecode(), url.MakeRelativeUri(author.Attributes["href"]?.Value ?? string.Empty)); } - - private static Seria GetSeria(HtmlDocument doc, Uri url) { + + private static Seria GetSeria(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("div.book-info a.book-info__link[href^=/serie/]"); - if (string.IsNullOrWhiteSpace(a.GetText())) { - return default; + if (string.IsNullOrWhiteSpace(a.GetText())) + { + return null; } var text = a.GetText(); var uri = url.MakeRelativeUri(a.Attributes["href"].Value); - if (!text.Contains('#')) { - return new Seria { - Name = text, - Url = uri + if (!text.Contains('#')) + { + return new Seria + { + Name = text, Url = uri }; } var parts = text.Split('#', StringSplitOptions.RemoveEmptyEntries); - return new Seria { - Name = parts[0].HtmlDecode(), - Number = parts[1].HtmlDecode(), - Url = uri + return new Seria + { + Name = parts[0].HtmlDecode(), Number = parts[1].HtmlDecode(), Url = uri }; - } - private static string GetBookId(Uri uri) { + private static string GetBookId(Uri uri) + { return uri.GetQueryParameter("b") ?? throw new InvalidOperationException("Не удалось определить идентификатор книги"); } - private async Task GetMainUrl(Uri url) { - if (GetId(url).StartsWith("chitat-online", StringComparison.InvariantCultureIgnoreCase)) { + private async Task GetMainUrl(Uri url) + { + if (GetId(url).StartsWith("chitat-online", StringComparison.InvariantCultureIgnoreCase)) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri($"/chitat-online/?b={GetBookId(url)}").ReplaceHost(_host.Host)); var href = doc.QuerySelector("h1 a").Attributes["href"].Value; return url.MakeRelativeUri(href).ReplaceHost(_host.Host); @@ -90,75 +95,95 @@ private async Task GetMainUrl(Uri url) { return url.ReplaceHost(_host.Host); } - private Task GetCover(HtmlNode doc, Uri bookUri) { + private Task GetCover(HtmlNode doc, Uri bookUri) + { var imagePath = doc.QuerySelector("img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath).ReplaceHost(_host.Host)) : Task.FromResult(default(TempFile)); } - private async Task AddChapter(ICollection chapters, Chapter chapter, StringBuilder text) { - if (chapter == null) { + private async Task AddChapter(ICollection chapters, Chapter chapter, StringBuilder text) + { + if (chapter == null) + { return; } - + var chapterDoc = text.AsHtmlDoc(); chapter.Images = await GetImages(chapterDoc, SystemUrl.MakeRelativeUri("/chitat-online/").ReplaceHost(_host.Host)); chapter.Content = chapterDoc.DocumentNode.InnerHtml; chapters.Add(chapter); } - private static bool IsSingleChapter(IEnumerable nodes) { + private static bool IsSingleChapter(IEnumerable nodes) + { var firstNode = nodes.First(); return firstNode.Name != "h3" || string.IsNullOrWhiteSpace(firstNode.InnerText); } - private static Chapter CreateChapter(string title) { - return new Chapter { + private static Chapter CreateChapter(string title) + { + return new Chapter + { Title = title }; } - private async Task GetChapter(string bookId, int page) { + private async Task GetChapter(string bookId, int page) + { var response = await Config.Client.GetWithTriesAsync(SystemUrl.MakeRelativeUri($"/chitat-online/?b={bookId}&pg={page}").ReplaceHost(_host.Host)); - if (response == default) { - return default; + if (response == null) + { + return null; } return await response.Content.ReadAsStreamAsync().ContinueWith(t => t.Result.AsHtmlDoc()); } - private async Task> FillChapters(string bookId, long pages, string name) { + private async Task> FillChapters(string bookId, long pages, string name) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - + Chapter chapter = null; var singleChapter = true; var text = new StringBuilder(); - - for (var i = 1; i <= pages; i++) { + + for (var i = 1; i <= pages; i++) + { Config.Logger.LogInformation($"Получаю страницу {i}/{pages}"); var page = await GetChapter(bookId, i); - if (page == default) { + if (page == null) + { Config.Logger.LogInformation("Неожиданный конец"); break; } - + var content = page.QuerySelector("div.reading__text").RemoveNodes("script, ins, div[id*=adfox]"); var nodes = content.QuerySelectorAll("> h3, > p, > img"); nodes = nodes.Count == 0 ? content.ChildNodes : nodes; singleChapter = i == 1 ? IsSingleChapter(nodes) : singleChapter; - foreach (var node in nodes) { - if (singleChapter || node.Name != "h3") { - if (node.Name == "img" && node.Attributes["src"] != null) { + foreach (var node in nodes) + { + if (singleChapter || node.Name != "h3") + { + if (node.Name == "img" && node.Attributes["src"] != null) + { text.Append($""); - } else { - if (!string.IsNullOrWhiteSpace(node.InnerHtml)) { + } + else + { + if (!string.IsNullOrWhiteSpace(node.InnerHtml)) + { text.Append(node.InnerHtml.HtmlDecode().CoverTag("p")); } } - } else { + } + else + { await AddChapter(result, chapter, text); text.Clear(); chapter = CreateChapter(node.InnerHtml.HtmlDecode()); @@ -169,4 +194,4 @@ private async Task> FillChapters(string bookId, long pages, string await AddChapter(result, chapter ?? CreateChapter(name), text); return result; } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Renovels/RemangaGetter.cs b/src/Core/Logic/Getters/Renovels/RemangaGetter.cs similarity index 73% rename from Core/Logic/Getters/Renovels/RemangaGetter.cs rename to src/Core/Logic/Getters/Renovels/RemangaGetter.cs index 3a769b22..9abb4ec3 100644 --- a/Core/Logic/Getters/Renovels/RemangaGetter.cs +++ b/src/Core/Logic/Getters/Renovels/RemangaGetter.cs @@ -1,4 +1,3 @@ -using System; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; @@ -7,23 +6,29 @@ using Core.Types.Renovels; using HtmlAgilityPack; -namespace Core.Logic.Getters.Renovels; +namespace Core.Logic.Getters.Renovels; -public class RemangaGetter(BookGetterConfig config) : RenovelsGetterBase(config) { +public class RemangaGetter(BookGetterConfig config) : RenovelsGetterBase(config) +{ protected override Uri SystemUrl => new("https://remanga.org/"); - + protected override string Segment => "manga"; - - protected override HtmlDocument GetChapterAsHtml(RenovelsChapter response) { + + protected override HtmlDocument GetChapterAsHtml(RenovelsChapter response) + { var sb = new StringBuilder(); - foreach (var obj in response.Pages) { - switch (obj) { + foreach (var obj in response.Pages) + { + switch (obj) + { case JsonObject: sb.Append($""); break; - case JsonArray pages: { - foreach (var page in pages) { + case JsonArray pages: + { + foreach (var page in pages) + { sb.Append($""); } @@ -34,4 +39,4 @@ protected override HtmlDocument GetChapterAsHtml(RenovelsChapter response) { return sb.AsHtmlDoc(); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Renovels/RenovelsGetter.cs b/src/Core/Logic/Getters/Renovels/RenovelsGetter.cs similarity index 79% rename from Core/Logic/Getters/Renovels/RenovelsGetter.cs rename to src/Core/Logic/Getters/Renovels/RenovelsGetter.cs index 6d24b89d..70b466df 100644 --- a/Core/Logic/Getters/Renovels/RenovelsGetter.cs +++ b/src/Core/Logic/Getters/Renovels/RenovelsGetter.cs @@ -1,17 +1,18 @@ -using System; using Core.Configs; using Core.Extensions; using Core.Types.Renovels; using HtmlAgilityPack; -namespace Core.Logic.Getters.Renovels; +namespace Core.Logic.Getters.Renovels; -public class RenovelsGetter(BookGetterConfig config) : RenovelsGetterBase(config) { +public class RenovelsGetter(BookGetterConfig config) : RenovelsGetterBase(config) +{ protected override Uri SystemUrl => new("https://renovels.org/"); - + protected override string Segment => "novel"; - - protected override HtmlDocument GetChapterAsHtml(RenovelsChapter response) { + + protected override HtmlDocument GetChapterAsHtml(RenovelsChapter response) + { return response.Content.AsHtmlDoc(); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Renovels/RenovelsGetterBase.cs b/src/Core/Logic/Getters/Renovels/RenovelsGetterBase.cs similarity index 78% rename from Core/Logic/Getters/Renovels/RenovelsGetterBase.cs rename to src/Core/Logic/Getters/Renovels/RenovelsGetterBase.cs index e5006bcd..923df0e7 100644 --- a/Core/Logic/Getters/Renovels/RenovelsGetterBase.cs +++ b/src/Core/Logic/Getters/Renovels/RenovelsGetterBase.cs @@ -1,11 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Nodes; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -14,65 +10,78 @@ using HtmlAgilityPack; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.Renovels; +namespace Core.Logic.Getters.Renovels; -public abstract class RenovelsGetterBase(BookGetterConfig config) : GetterBase(config) { +public abstract class RenovelsGetterBase(BookGetterConfig config) : GetterBase(config) +{ private Uri _apiUrl => new($"https://api.{SystemUrl.Host}/"); - + protected abstract string Segment { get; } - + protected abstract HtmlDocument GetChapterAsHtml(RenovelsChapter response); - protected override string GetId(Uri url) => url.GetSegment(2); + protected override string GetId(Uri url) + => url.GetSegment(2); - public override Task Init() { + public override Task Init() + { Config.Client.DefaultRequestHeaders.Add("User-Agent", "remanga/1.1.6 CFNetwork/1408.0.4 Darwin/22.5.0"); Config.Client.DefaultRequestHeaders.Add("Referer", SystemUrl.ToString()); return Task.CompletedTask; } - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } - - var payload = new { - user = Config.Options.Login, - password = Config.Options.Password + + var payload = new + { + user = Config.Options.Login, password = Config.Options.Password }; var response = await Config.Client.PostAsJsonAsync("https://api.renovels.org/api/users/login/".AsUri(), payload); var data = await response.Content.ReadFromJsonAsync>(); - if (string.IsNullOrWhiteSpace(data.Message)) { + if (string.IsNullOrWhiteSpace(data.Message)) + { var auth = data.Content.Deserialize(); Config.Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", auth.Token); Config.Logger.LogInformation("Успешно авторизовались"); - } else { + } + else + { throw new Exception($"Не удалось авторизоваться. {data.Message}"); } } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var bookId = GetId(url); var content = await GetContent(bookId); - var book = new Book(SystemUrl.MakeRelativeUri($"/{Segment}/{bookId}")) { + var book = new Book(SystemUrl.MakeRelativeUri($"/{Segment}/{bookId}")) + { Cover = await GetCover(content, url), Chapters = await FillChapters(content, url), Title = content.MainName ?? content.SecondaryName ?? content.AnotherName, Author = GetAuthor(content), Annotation = content.Description }; - + return book; } - - private Author GetAuthor(RenovelsContent content) { - if (content.Branches.Length == 0) { + + private Author GetAuthor(RenovelsContent content) + { + if (content.Branches.Length == 0) + { return new Author("Renovels"); } - - if (content.Branches[0].Publishers.Length == 0) { + + if (content.Branches[0].Publishers.Length == 0) + { return new Author("Renovels"); } @@ -80,23 +89,27 @@ private Author GetAuthor(RenovelsContent content) { return new Author(author.Name, SystemUrl.MakeRelativeUri($"/team/{author.Dir}")); } - private async Task GetContent(string bookId) { + private async Task GetContent(string bookId) + { var response = await Config.Client.GetFromJsonAsync(_apiUrl.MakeRelativeUri($"/api/v2/titles/{bookId}/")); return response; } - private async Task> FillChapters(RenovelsContent content, Uri url) { + private async Task> FillChapters(RenovelsContent content, Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - - foreach (var ranobeChapter in await GetToc(content)) { + + foreach (var ranobeChapter in await GetToc(content)) + { Config.Logger.LogInformation($"Загружаю главу {ranobeChapter.Title.CoverQuotes()}"); - + var chapter = new Chapter(); var doc = await GetChapter(ranobeChapter); - + chapter.Images = await GetImages(doc, url); chapter.Content = doc.DocumentNode.InnerHtml; chapter.Title = ranobeChapter.Title; @@ -107,29 +120,34 @@ private async Task> FillChapters(RenovelsContent content, U return result; } - private async Task GetChapter(RenovelsChapter ranobeChapter) { + private async Task GetChapter(RenovelsChapter ranobeChapter) + { var makeRelativeUri = _apiUrl.MakeRelativeUri($"/api/v2/titles/chapters/{ranobeChapter.Id}/"); var response = await Config.Client.GetFromJsonAsync(makeRelativeUri); return GetChapterAsHtml(response); } - private Task GetCover(RenovelsContent book, Uri bookUri) { + private Task GetCover(RenovelsContent book, Uri bookUri) + { var imagePath = book.Cover.GetValueOrDefault("high", null) ?? book.Cover.FirstOrDefault().Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - private async Task> GetToc(RenovelsContent content) { + private async Task> GetToc(RenovelsContent content) + { var result = new List(); - - for (var i = 1;; i++) { + + for (var i = 1;; i++) + { var uri = _apiUrl.MakeRelativeUri($"/api/v2/titles/chapters/?branch_id={content.Branches[0].Id}&ordering=-index&user_data=1&count=40&page={i}"); var response = await Config.Client.GetFromJsonAsync(uri); result.AddRange(response.Results); - if (response.Results.Length < 40) { + if (response.Results.Length < 40) + { result.Reverse(); return SliceToc(result.Where(c => !c.IsPaid || c.IsBought == true).ToList(), c => c.Name); } } } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/RomfantGetter.cs b/src/Core/Logic/Getters/RomfantGetter.cs similarity index 73% rename from Core/Logic/Getters/RomfantGetter.cs rename to src/Core/Logic/Getters/RomfantGetter.cs index 7d29d05d..fd41d230 100644 --- a/Core/Logic/Getters/RomfantGetter.cs +++ b/src/Core/Logic/Getters/RomfantGetter.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -12,77 +7,99 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class RomfantGetter(BookGetterConfig config) : GetterBase(config) { +public class RomfantGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://romfant.ru"); /// /// Авторизация в системе /// /// - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } - + using var post = await Config.Client.PostAsync(SystemUrl.MakeRelativeUri("ajax/"), GenerateAuthData()); var response = await post.Content.ReadAsStringAsync(); - if (response == "ok") { + if (response == "ok") + { Config.Logger.LogInformation("Успешно авторизовались"); - } else { + } + else + { throw new Exception($"Не удалось авторизоваться. {response.AsHtmlDoc().DocumentNode.GetText()}"); } } - private MultipartFormDataContent GenerateAuthData() { - return new() { - {new StringContent("auth"), "action"}, - {new StringContent(Config.Options.Login), "login"}, - {new StringContent(Config.Options.Password), "pass"}, + private MultipartFormDataContent GenerateAuthData() + { + return new() + { + { + new StringContent("auth"), "action" + }, + { + new StringContent(Config.Options.Login), "login" + }, + { + new StringContent(Config.Options.Password), "pass" + }, }; } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var bookId = GetId(url); url = SystemUrl.MakeRelativeUri($"/book/{bookId}/"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var title = doc.GetTextBySelector("h1"); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc), Chapters = await FillChapters(bookId, title), Title = title, Author = new Author("Витамина Мятная"), Annotation = doc.QuerySelector("div.subscribe div.text-center p")?.InnerHtml.HtmlDecode(), }; - + return book; } - private async Task> FillChapters(string bookId, string title) { - if (Config.Options.NoChapters) { + private async Task> FillChapters(string bookId, string title) + { + if (Config.Options.NoChapters) + { return []; } - + var chapter = new Chapter(); var sb = new StringBuilder(); var pages = await GetPages(bookId); - for (var i = 1; i <= pages; i++) { + for (var i = 1; i <= pages; i++) + { Config.Logger.LogInformation($"Получаю страницу {i}/{pages}"); var uri = SystemUrl.MakeRelativeUri($"/read/{bookId}/?page={i}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(uri); - if (doc.DocumentNode.InnerText.Contains("Чтобы продолжить чтение, пожалуйста, оплатите доступ.")) { + if (doc.DocumentNode.InnerText.Contains("Чтобы продолжить чтение, пожалуйста, оплатите доступ.")) + { Config.Logger.LogInformation("Платный контент"); break; } - foreach (var p in doc.QuerySelectorAll("div.book p")) { + foreach (var p in doc.QuerySelectorAll("div.book p")) + { var content = p.GetText(); - if (!string.IsNullOrWhiteSpace(content)) { + if (!string.IsNullOrWhiteSpace(content)) + { sb.AppendLine(content.CoverTag("p")); } } @@ -91,21 +108,26 @@ private async Task> FillChapters(string bookId, string titl chapter.Title = title; chapter.Content = sb.AsHtmlDoc().DocumentNode.InnerHtml; - return new[] { chapter }; + return new[] + { + chapter + }; } - private async Task GetPages(string bookId) { + private async Task GetPages(string bookId) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri($"/read/{bookId}/")); var pages = doc.QuerySelectorAll("div.navigation > a[href]") .Where(a => int.TryParse(a.GetText(), out _)) .Select(a => int.Parse(a.GetText())) .ToList(); - + return pages.Any() ? pages.Max() : 1; } - private Task GetCover(HtmlDocument doc) { + private Task GetCover(HtmlDocument doc) + { var imagePath = doc.QuerySelector("div.subscribe img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(SystemUrl.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/RoyalRoadGetter.cs b/src/Core/Logic/Getters/RoyalRoadGetter.cs similarity index 77% rename from Core/Logic/Getters/RoyalRoadGetter.cs rename to src/Core/Logic/Getters/RoyalRoadGetter.cs index 6e9a49ee..fc91ed8d 100644 --- a/Core/Logic/Getters/RoyalRoadGetter.cs +++ b/src/Core/Logic/Getters/RoyalRoadGetter.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -10,40 +6,47 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class RoyalRoadGetter(BookGetterConfig config) : GetterBase(config) { +public class RoyalRoadGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://royalroad.com/"); - protected override string GetId(Uri url) => url.GetSegment(2); + protected override string GetId(Uri url) + => url.GetSegment(2); - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { url = SystemUrl.MakeRelativeUri($"/fiction/{GetId(url)}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("h1"), Author = GetAuthor(doc, url), Annotation = doc.QuerySelector("div.description div.hidden-content")?.InnerHtml, }; - + return book; } - - private async Task> FillChapters(HtmlDocument doc, Uri url) { + + private async Task> FillChapters(HtmlDocument doc, Uri url) + { var result = new List(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var bookChapter in GetToc(doc, url)) { + foreach (var bookChapter in GetToc(doc, url)) + { var chapter = new Chapter(); Config.Logger.LogInformation($"Загружаю главу {bookChapter.Title.CoverQuotes()}"); - + var chapterDoc = await GetChapter(bookChapter.Url); - + chapter.Title = bookChapter.Title; chapter.Images = await GetImages(chapterDoc, url); chapter.Content = chapterDoc.DocumentNode.InnerHtml.HtmlDecode(); @@ -54,36 +57,41 @@ private async Task> FillChapters(HtmlDocument doc, Uri url) return result; } - private async Task GetChapter(Uri url) { + private async Task GetChapter(Uri url) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var content = doc.QuerySelector("div.chapter-content"); - foreach (var node in content.QuerySelectorAll("p, span")) { + foreach (var node in content.QuerySelectorAll("p, span")) + { node.Attributes.RemoveAll(); } return content.InnerHtml.AsHtmlDoc(); } - private IEnumerable GetToc(HtmlDocument doc, Uri url) { + private IEnumerable GetToc(HtmlDocument doc, Uri url) + { var result = doc .QuerySelectorAll("tr.chapter-row") .Select(r => r.QuerySelector("a[href^=/fiction/]")) .Select(a => new UrlChapter(url.MakeRelativeUri(a.Attributes["href"].Value), a.GetText())) .ToList(); - + return SliceToc(result, c => c.Title); } - private Author GetAuthor(HtmlDocument doc, Uri url) { + private Author GetAuthor(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("div.fic-header h4 a[href*=profile]"); - return a == default ? - new Author("Royalroad") : + return a == null ? + new Author("Royalroad") : new Author(a.GetText(), SystemUrl.MakeRelativeUri(a.Attributes["href"].Value)); } - private Task GetCover(HtmlDocument doc, Uri uri) { + private Task GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("div.cover-art-container img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Rulate/BllateGetter.cs b/src/Core/Logic/Getters/Rulate/BllateGetter.cs similarity index 88% rename from Core/Logic/Getters/Rulate/BllateGetter.cs rename to src/Core/Logic/Getters/Rulate/BllateGetter.cs index 70797e58..f84afbc1 100644 --- a/Core/Logic/Getters/Rulate/BllateGetter.cs +++ b/src/Core/Logic/Getters/Rulate/BllateGetter.cs @@ -1,9 +1,9 @@ -using System; using Core.Configs; namespace Core.Logic.Getters.Rulate; -public class BllateGetter(BookGetterConfig config) : RulateGetterBase(config) { +public class BllateGetter(BookGetterConfig config) : RulateGetterBase(config) +{ protected override Uri SystemUrl => new("https://bllate.org"); protected override string Mature { get; } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Rulate/ErolateGetter.cs b/src/Core/Logic/Getters/Rulate/ErolateGetter.cs similarity index 77% rename from Core/Logic/Getters/Rulate/ErolateGetter.cs rename to src/Core/Logic/Getters/Rulate/ErolateGetter.cs index bd2321d9..1fc03223 100644 --- a/Core/Logic/Getters/Rulate/ErolateGetter.cs +++ b/src/Core/Logic/Getters/Rulate/ErolateGetter.cs @@ -1,9 +1,9 @@ -using System; using Core.Configs; -namespace Core.Logic.Getters.Rulate; +namespace Core.Logic.Getters.Rulate; -public class ErolateGetter(BookGetterConfig config) : RulateGetterBase(config) { +public class ErolateGetter(BookGetterConfig config) : RulateGetterBase(config) +{ protected override Uri SystemUrl => new("https://erolate.com/"); protected override string Mature => "7da3ee594b38fc5355692d978fe8f5adbeb3d17di%3A1%3B"; -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Rulate/MlateGetter.cs b/src/Core/Logic/Getters/Rulate/MlateGetter.cs similarity index 78% rename from Core/Logic/Getters/Rulate/MlateGetter.cs rename to src/Core/Logic/Getters/Rulate/MlateGetter.cs index da495bfc..0027d68e 100644 --- a/Core/Logic/Getters/Rulate/MlateGetter.cs +++ b/src/Core/Logic/Getters/Rulate/MlateGetter.cs @@ -1,9 +1,9 @@ -using System; using Core.Configs; -namespace Core.Logic.Getters.Rulate; +namespace Core.Logic.Getters.Rulate; -public class MlateGetter(BookGetterConfig config) : RulateGetterBase(config) { +public class MlateGetter(BookGetterConfig config) : RulateGetterBase(config) +{ protected override Uri SystemUrl => new("https://mlate.ru/"); protected override string Mature => "c3a2ed4b199a1a15f5a5483504c7a75a7030dc4bi%3A1%3B"; -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Rulate/RulateGetter.cs b/src/Core/Logic/Getters/Rulate/RulateGetter.cs similarity index 78% rename from Core/Logic/Getters/Rulate/RulateGetter.cs rename to src/Core/Logic/Getters/Rulate/RulateGetter.cs index b8e174f6..098aa55c 100644 --- a/Core/Logic/Getters/Rulate/RulateGetter.cs +++ b/src/Core/Logic/Getters/Rulate/RulateGetter.cs @@ -1,9 +1,9 @@ -using System; using Core.Configs; -namespace Core.Logic.Getters.Rulate; +namespace Core.Logic.Getters.Rulate; -public class RulateGetter(BookGetterConfig config) : RulateGetterBase(config) { +public class RulateGetter(BookGetterConfig config) : RulateGetterBase(config) +{ protected override Uri SystemUrl => new("https://tl.rulate.ru"); protected override string Mature => "c3a2ed4b199a1a15f5a5483504c7a75a7030dc4bi%3A1%3B"; -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/Rulate/RulateGetterBase.cs b/src/Core/Logic/Getters/Rulate/RulateGetterBase.cs similarity index 70% rename from Core/Logic/Getters/Rulate/RulateGetterBase.cs rename to src/Core/Logic/Getters/Rulate/RulateGetterBase.cs index 6d38c501..0ca43773 100644 --- a/Core/Logic/Getters/Rulate/RulateGetterBase.cs +++ b/src/Core/Logic/Getters/Rulate/RulateGetterBase.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Net.Http; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -14,109 +9,131 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.Rulate; +namespace Core.Logic.Getters.Rulate; + +public abstract class RulateGetterBase(BookGetterConfig config) : GetterBase(config) +{ + protected override string GetId(Uri url) + => url.Segments.Length == 3 ? base.GetId(url) : url.GetSegment(2); -public abstract class RulateGetterBase(BookGetterConfig config) : GetterBase(config) { - protected override string GetId(Uri url) => url.Segments.Length == 3 ? base.GetId(url) : url.GetSegment(2); - protected abstract string Mature { get; } - - public override async Task Init() { + + public override async Task Init() + { await base.Init(); Config.Client.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest"); Config.CookieContainer.Add(SystemUrl, new Cookie("mature", Mature)); } - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } - + var doc = await Config.Client.PostHtmlDocWithTriesAsync(SystemUrl, GetAuthData()); var alertBlock = doc.QuerySelector("div.alert.in.alert-block"); - - if (alertBlock == default) { + + if (alertBlock == null) + { RulateAuthResponse error = null; - try { + try + { error = doc.ParsedText.Deserialize(); - } catch { } + } + catch { } - if (!string.IsNullOrWhiteSpace(error?.Error)) { - throw new Exception($"Не удалось авторизоваться. {error.Error}"); + if (!string.IsNullOrWhiteSpace(error?.Error)) + { + throw new Exception($"Не удалось авторизоваться. {error.Error}"); } - + Config.Logger.LogInformation("Успешно авторизовались"); - } else { - throw new Exception($"Не удалось авторизоваться. {alertBlock.GetText()}"); + } + else + { + throw new Exception($"Не удалось авторизоваться. {alertBlock.GetText()}"); } } - - private FormUrlEncodedContent GetAuthData() { - var data = new Dictionary { - ["login[login]"] = Config.Options.Login, - ["login[pass]"] = Config.Options.Password, + + private FormUrlEncodedContent GetAuthData() + { + var data = new Dictionary + { + ["login[login]"] = Config.Options.Login, ["login[pass]"] = Config.Options.Password, }; return new FormUrlEncodedContent(data); } - public override async Task Get(Uri url) { + public override async Task Get(Uri url) + { var bookId = GetId(url); url = SystemUrl.MakeRelativeUri($"/book/{bookId}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url, bookId), Title = GetTitle(doc), Author = GetAuthor(doc), Annotation = doc.QuerySelector("#Info div.btn-toolbar + div")?.InnerHtml }; - + return book; } - private static string GetTitle(HtmlDocument doc) { + private static string GetTitle(HtmlDocument doc) + { var match = Regex.Match(doc.ParsedText, "t_title: '(?.*?)',"); return match.Success ? match.Groups["title"].Value : doc.GetTextBySelector("h1"); } - private Author GetAuthor(HtmlDocument doc) { + private Author GetAuthor(HtmlDocument doc) + { var def = new Author("rulate"); - foreach (var p in doc.QuerySelectorAll("#Info p")) { + foreach (var p in doc.QuerySelectorAll("#Info p")) + { var strong = p.QuerySelector("strong"); - if (strong != null && strong.InnerText.Contains("Автор")) { + if (strong != null && strong.InnerText.Contains("Автор")) + { var em = p.QuerySelector("em"); - if (em == default) { + if (em == null) + { return def; } - + var a = em.QuerySelector("a[href]"); - return a == default ? - new Author(em.GetText()) : + return a == null ? + new Author(em.GetText()) : new Author(a.GetText(), SystemUrl.MakeRelativeUri(a.Attributes["href"].Value)); - } } return def; } - - private Task<TempFile> GetCover(HtmlDocument doc, Uri bookUri) { + + private Task<TempFile> GetCover(HtmlDocument doc, Uri bookUri) + { var imagePath = doc.QuerySelector("div.slick img")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(bookUri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - - private async Task<List<Chapter>> FillChapters(HtmlDocument doc, Uri bookUri, string bookId) { + + private async Task<List<Chapter>> FillChapters(HtmlDocument doc, Uri bookUri, string bookId) + { var result = new List<Chapter>(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - - foreach (var (id, name) in GetToc(doc)) { + + foreach (var (id, name) in GetToc(doc)) + { Config.Logger.LogInformation($"Загружаю главу {name.CoverQuotes()}"); var chapter = new Chapter(); - + var chapterDoc = await GetChapter(bookId, id); chapter.Images = await GetImages(chapterDoc, bookUri); chapter.Content = chapterDoc.DocumentNode.InnerHtml; @@ -128,16 +145,18 @@ private async Task<List<Chapter>> FillChapters(HtmlDocument doc, Uri bookUri, st return result; } - private async Task<HtmlDocument> GetChapter(string bookId, string chapterId) { + private async Task<HtmlDocument> GetChapter(string bookId, string chapterId) + { var s = await Config.Client.GetFromJsonWithTriesAsync<RulateChapter>(SystemUrl.MakeRelativeUri($"/book/{bookId}/{chapterId}/readyajax")); return (s.CanRead ? s.Content.AsHtmlDoc().QuerySelector("div.content-text")?.InnerHtml ?? string.Empty : string.Empty).AsHtmlDoc(); } - private IEnumerable<IdChapter> GetToc(HtmlDocument doc) { + private IEnumerable<IdChapter> GetToc(HtmlDocument doc) + { var result = doc.QuerySelectorAll("#Chapters tr[data-id]") .Select(chapter => new IdChapter(chapter.Attributes["data-id"].Value, chapter.GetTextBySelector("td.t"))) .ToList(); - + return SliceToc(result, c => c.Title); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/SamlibGetter.cs b/src/Core/Logic/Getters/SamlibGetter.cs similarity index 76% rename from Core/Logic/Getters/SamlibGetter.cs rename to src/Core/Logic/Getters/SamlibGetter.cs index f87971ed..70877bea 100644 --- a/Core/Logic/Getters/SamlibGetter.cs +++ b/src/Core/Logic/Getters/SamlibGetter.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -11,9 +7,10 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class SamlibGetter(BookGetterConfig config) : GetterBase(config) { +public class SamlibGetter(BookGetterConfig config) : GetterBase(config) +{ private const string START_BOOK_PATTERN = "Собственно произведение"; private const string ABOUT_BLOCK_PATTERN = "Блок описания произведения"; private const string END_BOOK_PATTERN = "-----------------------------------------------"; @@ -24,43 +21,49 @@ public class SamlibGetter(BookGetterConfig config) : GetterBase(config) { private static Encoding _encoding = Encoding.GetEncoding(1251); protected override Uri SystemUrl => new("http://samlib.ru/"); - public override async Task<Book> Get(Uri url) { + + public override async Task<Book> Get(Uri url) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(url, _encoding); var title = doc.GetTextBySelector("h2, h3 font"); - var book = new Book(url) { - Cover = null, - Chapters = await FillChapters(doc, url, title), - Title = title, - Author = GetAuthor(doc, url) + var book = new Book(url) + { + Cover = null, Chapters = await FillChapters(doc, url, title), Title = title, Author = GetAuthor(doc, url) }; - + return book; } - - private static Author GetAuthor(HtmlDocument doc, Uri url) { + + private static Author GetAuthor(HtmlDocument doc, Uri url) + { var def = new Author("Samlib"); - + var h3 = doc.QuerySelector("h3"); - if (h3 == default) { + if (h3 == null) + { return def; } var a = h3.QuerySelector("a[href]"); - if (a == default) { + if (a == null) + { return def; } return new Author(h3.RemoveNodes("small").GetText().Trim(':'), url.MakeRelativeUri(a.Attributes["href"].Value)); } - private async Task<IEnumerable<Chapter>> FillChapters(HtmlDocument doc, Uri url, string title) { + private async Task<IEnumerable<Chapter>> FillChapters(HtmlDocument doc, Uri url, string title) + { var result = new List<Chapter>(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - - foreach (var urlChapter in GetToc(doc, url, title)) { + + foreach (var urlChapter in GetToc(doc, url, title)) + { Config.Logger.LogInformation($"Загружаю главу {urlChapter.Title.CoverQuotes()}"); result.Add(await GetChapter(urlChapter)); } @@ -68,28 +71,37 @@ private async Task<IEnumerable<Chapter>> FillChapters(HtmlDocument doc, Uri url, return result; } - private IEnumerable<UrlChapter> GetToc(HtmlDocument doc, Uri url, string title) { + private IEnumerable<UrlChapter> GetToc(HtmlDocument doc, Uri url, string title) + { var content = GetStringBetween(doc.Text, START_LINK_BLOCK_PATTERN, END_LINK_BLOCK_PATTERN); - if (string.IsNullOrWhiteSpace(content)) { + if (string.IsNullOrWhiteSpace(content)) + { yield return new UrlChapter(url, title); - } else { - foreach (var a in content.AsHtmlDoc().QuerySelectorAll("li > a")) { + } + else + { + foreach (var a in content.AsHtmlDoc().QuerySelectorAll("li > a")) + { yield return new UrlChapter(url.MakeRelativeUri(a.Attributes["href"].Value), a.GetText()); } } } - private static string GetStringBetween(string origin, string startPattern, string endPattern) { + private static string GetStringBetween(string origin, string startPattern, string endPattern) + { var start = origin.IndexOf(startPattern, StringComparison.InvariantCultureIgnoreCase); - if (start == -1) { + if (start == -1) + { return string.Empty; } - + start = origin.IndexOf(">", start, StringComparison.InvariantCultureIgnoreCase) + 1; var stop = origin.IndexOf(endPattern, start, StringComparison.InvariantCultureIgnoreCase); - for (var i = stop;; i--) { - if (origin[i] == '<') { + for (var i = stop;; i--) + { + if (origin[i] == '<') + { stop = i - 1; break; } @@ -97,23 +109,28 @@ private static string GetStringBetween(string origin, string startPattern, strin return origin[start..stop]; } - - private static string GetBookContent(string origin) { + + private static string GetBookContent(string origin) + { var start = origin.IndexOf(START_BOOK_PATTERN, StringComparison.InvariantCultureIgnoreCase); - if (start == -1) { + if (start == -1) + { return string.Empty; } var about = origin.LastIndexOf(ABOUT_BLOCK_PATTERN, StringComparison.OrdinalIgnoreCase); - if (about == -1) { + if (about == -1) + { return string.Empty; } - + start = origin.IndexOf(">", start, StringComparison.InvariantCultureIgnoreCase) + 1; var stop = origin.LastIndexOf(END_BOOK_PATTERN, about, StringComparison.InvariantCultureIgnoreCase); - - for (var i = stop;; i--) { - if (origin[i] == '<') { + + for (var i = stop;; i--) + { + if (origin[i] == '<') + { stop = i - 1; break; } @@ -122,32 +139,38 @@ private static string GetBookContent(string origin) { return origin[start..stop].HtmlDecode(); } - private async Task<Chapter> GetChapter(UrlChapter urlChapter) { + private async Task<Chapter> GetChapter(UrlChapter urlChapter) + { var chapter = new Chapter(); var doc = await Config.Client.GetHtmlDocWithTriesAsync(urlChapter.Url, _encoding); doc.LoadHtml(GetBookContent(doc.Text)); - + var sr = new StringReader(doc.DocumentNode.InnerHtml.HtmlDecode()); var text = new StringBuilder(); - while (true) { + while (true) + { var line = await sr.ReadLineAsync(); - if (line == null) { + if (line == null) + { break; } - if (string.IsNullOrWhiteSpace(line)) { + if (string.IsNullOrWhiteSpace(line)) + { continue; } - + var htmlDoc = line.AsHtmlDoc(); - foreach (var node in htmlDoc.DocumentNode.ChildNodes) { - if (!string.IsNullOrWhiteSpace(node.InnerText) || node.QuerySelector("img") != null) { + foreach (var node in htmlDoc.DocumentNode.ChildNodes) + { + if (!string.IsNullOrWhiteSpace(node.InnerText) || node.QuerySelector("img") != null) + { text.Append(node.InnerHtml.HtmlDecode().CoverTag("p")); } } } - + var chapterDoc = text.AsHtmlDoc(); chapter.Images = await GetImages(chapterDoc, urlChapter.Url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; @@ -155,4 +178,4 @@ private async Task<Chapter> GetChapter(UrlChapter urlChapter) { return chapter; } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/StrokiMtsGetter.cs b/src/Core/Logic/Getters/StrokiMtsGetter.cs similarity index 71% rename from Core/Logic/Getters/StrokiMtsGetter.cs rename to src/Core/Logic/Getters/StrokiMtsGetter.cs index 80e0bd0e..caf90946 100644 --- a/Core/Logic/Getters/StrokiMtsGetter.cs +++ b/src/Core/Logic/Getters/StrokiMtsGetter.cs @@ -1,13 +1,8 @@ -using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Net.Http; using System.Net.Http.Json; using System.Security.Cryptography; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Misc; @@ -18,113 +13,140 @@ namespace Core.Logic.Getters; -public class StrokiMtsGetter(BookGetterConfig config) : GetterBase(config) { +public class StrokiMtsGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://stroki.mts.ru/"); - protected override string GetId(Uri url) { + protected override string GetId(Uri url) + { return url.GetSegment(2).Split("-").Last(); } - public override Task Authorize() { + public override Task Authorize() + { var token = Config.Options.Login ?? Config.Options.Password; - if (string.IsNullOrWhiteSpace(token)) { + if (string.IsNullOrWhiteSpace(token)) + { throw new Exception("Не указан авторизационный token."); } - + Config.Client.DefaultRequestHeaders.Add("access-token", token); return Task.CompletedTask; } - - public override async Task<Book> Get(Uri url) { + + public override async Task<Book> Get(Uri url) + { var id = GetId(url); - + var details = await GetBook(id); - var detailBook = details?.Items.FirstOrDefault(d => d.TextBook != default)?.TextBook ?? - details?.Items.FirstOrDefault(d => d.AudioBook != default)?.AudioBook ?? - details?.Items.FirstOrDefault(d => d.ComicBook != default)?.ComicBook ?? - details?.Items.FirstOrDefault(d => d.PressBook != default)?.PressBook ?? + var detailBook = details?.Items.FirstOrDefault(d => d.TextBook != null)?.TextBook ?? + details?.Items.FirstOrDefault(d => d.AudioBook != null)?.AudioBook ?? + details?.Items.FirstOrDefault(d => d.ComicBook != null)?.ComicBook ?? + details?.Items.FirstOrDefault(d => d.PressBook != null)?.PressBook ?? await GetSerial(id); - if (detailBook == default) { + if (detailBook == null) + { throw new Exception("Не удалось получить книгу"); } - - var book = new Book(url) { + + var book = new Book(url) + { Cover = await GetCover(detailBook), Title = detailBook.Title, Author = GetAuthor(detailBook), CoAuthors = GetCoAuthors(detailBook), Annotation = detailBook.Annotation, }; - - if (Config.Options.NoChapters) { + + if (Config.Options.NoChapters) + { return book; } - + var origBooks = await GetOriginalFiles(id); - if (origBooks.Count == 1) { - if (detailBook.ContentInfo.FileType == "EPUB") { + if (origBooks.Count == 1) + { + if (detailBook.ContentInfo.FileType == "EPUB") + { book.Chapters = await FillChaptersFromEpub(origBooks[0]); - } else { + } + else + { Config.Logger.LogInformation("Эта книга не в формате epub. Получить главы невозможно"); } - } else { - if (detailBook.ContentInfo.FileType == "JPG") { + } + else + { + if (detailBook.ContentInfo.FileType == "JPG") + { book.Chapters = FillChaptersFromImages(detailBook, origBooks); } } - - if (Config.Options.HasAdditionalType(AdditionalTypeEnum.Books)) { - foreach (var origBook in origBooks) { + + if (Config.Options.HasAdditionalType(AdditionalTypeEnum.Books)) + { + foreach (var origBook in origBooks) + { book.AdditionalFiles.Add(AdditionalTypeEnum.Books, origBook); } } - if (Config.Options.HasAdditionalType(AdditionalTypeEnum.Audio)) { + if (Config.Options.HasAdditionalType(AdditionalTypeEnum.Audio)) + { book.AdditionalFiles.Add(AdditionalTypeEnum.Audio, await GetAudio(details)); } - + return book; } - private static IEnumerable<Chapter> FillChaptersFromImages(StrokiMtsBookItem book, IEnumerable<TempFile> images) { + private static IEnumerable<Chapter> FillChaptersFromImages(StrokiMtsBookItem book, IEnumerable<TempFile> images) + { var sb = new StringBuilder(); - foreach (var image in images) { + foreach (var image in images) + { sb.Append($"<img src=\"{image.FullName}\" />"); } - - var chapter = new Chapter { - Images = images, - Content = sb.ToString(), - Title = book.Title, + + var chapter = new Chapter + { + Images = images, Content = sb.ToString(), Title = book.Title, + }; + + return new List<Chapter> + { + chapter }; - - return new List<Chapter>{chapter}; } - private async Task<List<TempFile>> GetOriginalFiles(string bookId) { + private async Task<List<TempFile>> GetOriginalFiles(string bookId) + { var fileMetas = await GetFileMeta(bookId); var result = new ConcurrentBag<Tuple<TempFile, int>>(); - + var tuples = fileMetas.Select((file, i) => Tuple.Create(file, i)); - await Parallel.ForEachAsync(tuples, async (t, _) => { - var fileUrl = await GetFileUrl(t.Item1); - var response = await Config.Client.GetWithTriesAsync(fileUrl.Url.AsUri()); - Config.Logger.LogInformation($"Оригинальный файл доступен по ссылке {fileUrl.Url.AsUri()}"); - var tempFile = await TempFile.Create(fileUrl.Url.AsUri(), Config.TempFolder.Path, fileUrl.Url.AsUri().GetFileName(), await response.Content.ReadAsStreamAsync(_)); - - result.Add(Tuple.Create(tempFile, t.Item2)); - }); - + await Parallel.ForEachAsync( + tuples, + async (t, _) => + { + var fileUrl = await GetFileUrl(t.Item1); + var response = await Config.Client.GetWithTriesAsync(fileUrl.Url.AsUri()); + Config.Logger.LogInformation($"Оригинальный файл доступен по ссылке {fileUrl.Url.AsUri()}"); + var tempFile = await TempFile.Create(fileUrl.Url.AsUri(), Config.TempFolder.Path, fileUrl.Url.AsUri().GetFileName(), await response.Content.ReadAsStreamAsync(_)); + + result.Add(Tuple.Create(tempFile, t.Item2)); + }); return result.OrderBy(t => t.Item2).Select(t => t.Item1).ToList(); } - private async Task<List<TempFile>> GetAudio(StrokiMtsApiMultiResponse details) { + private async Task<List<TempFile>> GetAudio(StrokiMtsApiMultiResponse details) + { var result = new List<TempFile>(); - var detail = details?.Items.FirstOrDefault(d => d.AudioBook != default); - if (detail == default) { + var detail = details?.Items.FirstOrDefault(d => d.AudioBook != null); + if (detail == null) + { return result; } @@ -132,7 +154,8 @@ private async Task<List<TempFile>> GetAudio(StrokiMtsApiMultiResponse details) { var fileMetas = await GetFileMeta(audio.Id.ToString()); - for (var i = 0; i < fileMetas.Length; i++) { + for (var i = 0; i < fileMetas.Length; i++) + { var meta = fileMetas[i]; var fileUrl = await GetFileUrl(meta); @@ -144,72 +167,84 @@ private async Task<List<TempFile>> GetAudio(StrokiMtsApiMultiResponse details) { return result; } - - private Task<TempFile> GetCover(StrokiMtsBookItem book) { + + private Task<TempFile> GetCover(StrokiMtsBookItem book) + { var url = book.ImageUrl.TryGetValue("extraLarge", out var item) ? item : book.ImageUrl.FirstOrDefault().Value; return !string.IsNullOrWhiteSpace(url) ? SaveImage(SystemUrl.MakeRelativeUri(url)) : Task.FromResult(default(TempFile)); } - - private Author GetAuthor(StrokiMtsBookItem book) { + + private Author GetAuthor(StrokiMtsBookItem book) + { var author = book.Authors.FirstOrDefault(); - return author == default ? new Author("Stroki") : new Author(author.Name, SystemUrl.MakeRelativeUri(author.FriendlyUrl)); + return author == null ? new Author("Stroki") : new Author(author.Name, SystemUrl.MakeRelativeUri(author.FriendlyUrl)); } - - private IEnumerable<Author> GetCoAuthors(StrokiMtsBookItem book) { + + private IEnumerable<Author> GetCoAuthors(StrokiMtsBookItem book) + { return book.Authors.Skip(1).Select(author => new Author(author.Name, SystemUrl.MakeRelativeUri(author.FriendlyUrl))).ToList(); } - private async Task<StrokiMtsFile[]> GetFileMeta(string id) { + private async Task<StrokiMtsFile[]> GetFileMeta(string id) + { var json = await SendAsync<StrokiMtsApiResponse<StrokiMtsFiles>>(() => GetMessage(SystemUrl.MakeRelativeUri("/api/books/files").AppendQueryParameter("bookId", id), "5.0", "5.0")); return json.Data.Full?.Length > 0 ? json.Data.Full : [json.Data.Preview]; } - - private async Task<StrokiMtsFileUrl> GetFileUrl(StrokiMtsFile file) { + + private async Task<StrokiMtsFileUrl> GetFileUrl(StrokiMtsFile file) + { var json = await SendAsync<StrokiMtsApiResponse<StrokiMtsFileUrl>>(() => GetMessage(SystemUrl.MakeRelativeUri($"api/books/files/data/link/{file.FileId}"), "5.0", "5.0")); return json.Data; } - private async Task<StrokiMtsApiMultiResponse> GetBook(string id) { + private async Task<StrokiMtsApiMultiResponse> GetBook(string id) + { var json = await SendAsync<StrokiMtsApiResponse<StrokiMtsApiMultiResponse>>(() => GetMessage(SystemUrl.MakeRelativeUri($"/api/books/multi/{id}"), "5.53", "7.0")); return json.Data; } - - private async Task<StrokiMtsBookItem> GetSerial(string id) { + + private async Task<StrokiMtsBookItem> GetSerial(string id) + { var json = await SendAsync<StrokiMtsApiResponse<StrokiMtsBookItem>>(() => GetMessage(SystemUrl.MakeRelativeUri($"/api/serials/episodes/{id} "), "5.53", "7.0")); return json.Data; } - private async Task<T> SendAsync<T>(Func<HttpRequestMessage> message) { + private async Task<T> SendAsync<T>(Func<HttpRequestMessage> message) + { var response = await Config.Client.SendWithTriesAsync(message); - if (response.StatusCode != HttpStatusCode.OK) { + if (response.StatusCode != HttpStatusCode.OK) + { throw new Exception(await response.Content.ReadAsStringAsync()); } - + return await response.Content.ReadFromJsonAsync<T>(); } - - protected virtual HttpRequestMessage GetMessage(Uri uri, string appVersion, string apiVerions) { + + protected virtual HttpRequestMessage GetMessage(Uri uri, string appVersion, string apiVerions) + { var message = new HttpRequestMessage(HttpMethod.Get, uri); - foreach (var header in Config.Client.DefaultRequestHeaders) { + foreach (var header in Config.Client.DefaultRequestHeaders) + { message.Headers.Add(header.Key, header.Value); } - + message.Headers.Add("install-guid", Guid.NewGuid().ToString()); message.Headers.Add("app-version", appVersion); message.Headers.Add("language", "ru"); message.Headers.Add("platform", "ios"); message.Headers.Add("api-version", apiVerions); - + message.Headers.Add("signature", GetSignature(uri)); - + return message; } - private static string GetSignature(Uri url) { + private static string GetSignature(Uri url) + { var inputBytes = Encoding.UTF8.GetBytes(url.ToString().Replace("https://", "http://") + "meg@$h!t"); var hashBytes = MD5.HashData(inputBytes); return Convert.ToHexString(hashBytes).ToLower(); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/TopLiba/BiglibaGetter.cs b/src/Core/Logic/Getters/TopLiba/BiglibaGetter.cs similarity index 60% rename from Core/Logic/Getters/TopLiba/BiglibaGetter.cs rename to src/Core/Logic/Getters/TopLiba/BiglibaGetter.cs index d99d06f9..7053155f 100644 --- a/Core/Logic/Getters/TopLiba/BiglibaGetter.cs +++ b/src/Core/Logic/Getters/TopLiba/BiglibaGetter.cs @@ -1,6 +1,3 @@ -using System; -using System.Linq; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -8,36 +5,40 @@ using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; -namespace Core.Logic.Getters.TopLiba; +namespace Core.Logic.Getters.TopLiba; -public class BiglibaGetter(BookGetterConfig config) : TopLibaGetterBase(config) { +public class BiglibaGetter(BookGetterConfig config) : TopLibaGetterBase(config) +{ protected override Uri SystemUrl => new("https://bigliba.com/"); - protected override Seria GetSeria(HtmlDocument doc, Uri url) { + protected override Seria GetSeria(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("div.book-series a"); - if (a != default) { + if (a != null) + { var text = a.GetText(); - - if (text.Contains('#')) { + + if (text.Contains('#')) + { var parts = text.Split(':').Last().Split("(#"); - return new Seria { - Name = parts[0].Trim(), - Number = parts[1].Trim(')').Trim(), - Url = url.MakeRelativeUri(a.Attributes["href"].Value) + return new Seria + { + Name = parts[0].Trim(), Number = parts[1].Trim(')').Trim(), Url = url.MakeRelativeUri(a.Attributes["href"].Value) }; } - return new Seria { - Name = text, - Url = url.MakeRelativeUri(a.Attributes["href"].Value) + return new Seria + { + Name = text, Url = url.MakeRelativeUri(a.Attributes["href"].Value) }; } - return default; + return null; } - protected override Task<TempFile> GetCover(HtmlDocument doc, Uri uri) { + protected override Task<TempFile> GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("img[itemprop=image]")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/TopLiba/TopLibaGetter.cs b/src/Core/Logic/Getters/TopLiba/TopLibaGetter.cs similarity index 57% rename from Core/Logic/Getters/TopLiba/TopLibaGetter.cs rename to src/Core/Logic/Getters/TopLiba/TopLibaGetter.cs index 97d00494..44e76011 100644 --- a/Core/Logic/Getters/TopLiba/TopLibaGetter.cs +++ b/src/Core/Logic/Getters/TopLiba/TopLibaGetter.cs @@ -1,5 +1,3 @@ -using System; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -7,36 +5,40 @@ using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; -namespace Core.Logic.Getters.TopLiba; +namespace Core.Logic.Getters.TopLiba; -public class TopLibaGetter(BookGetterConfig config) : TopLibaGetterBase(config) { +public class TopLibaGetter(BookGetterConfig config) : TopLibaGetterBase(config) +{ protected override Uri SystemUrl => new("https://topliba.com/"); - protected override Seria GetSeria(HtmlDocument doc, Uri url) { + protected override Seria GetSeria(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("div.book-series a"); - if (a != default) { + if (a != null) + { var text = a.GetText(); var number = a.NextSibling; - - if (number != default && number.GetText().Contains('#')) { - return new Seria { - Name = text, - Number = number.GetText().Replace("(", "").Replace(")", "").Replace("#", ""), - Url = url.MakeRelativeUri(a.Attributes["href"].Value) + + if (number != null && number.GetText().Contains('#')) + { + return new Seria + { + Name = text, Number = number.GetText().Replace("(", "").Replace(")", "").Replace("#", ""), Url = url.MakeRelativeUri(a.Attributes["href"].Value) }; } - return new Seria { - Name = text, - Url = url.MakeRelativeUri(a.Attributes["href"].Value) + return new Seria + { + Name = text, Url = url.MakeRelativeUri(a.Attributes["href"].Value) }; } - return default; + return null; } - protected override Task<TempFile> GetCover(HtmlDocument doc, Uri uri) { + protected override Task<TempFile> GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("img[itemprop=contentUrl]")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/TopLiba/TopLibaGetterBase.cs b/src/Core/Logic/Getters/TopLiba/TopLibaGetterBase.cs similarity index 72% rename from Core/Logic/Getters/TopLiba/TopLibaGetterBase.cs rename to src/Core/Logic/Getters/TopLiba/TopLibaGetterBase.cs index bbc18843..a635e6b8 100644 --- a/Core/Logic/Getters/TopLiba/TopLibaGetterBase.cs +++ b/src/Core/Logic/Getters/TopLiba/TopLibaGetterBase.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -12,22 +7,26 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.TopLiba; +namespace Core.Logic.Getters.TopLiba; -public abstract class TopLibaGetterBase(BookGetterConfig config) : GetterBase(config) { - protected override string GetId(Uri url) => url.GetSegment(2); +public abstract class TopLibaGetterBase(BookGetterConfig config) : GetterBase(config) +{ + protected override string GetId(Uri url) + => url.GetSegment(2); protected abstract Seria GetSeria(HtmlDocument doc, Uri url); protected abstract Task<TempFile> GetCover(HtmlDocument doc, Uri uri); - - public override async Task<Book> Get(Uri url) { + + public override async Task<Book> Get(Uri url) + { var bookId = GetId(url); url = SystemUrl.MakeRelativeUri($"/books/{bookId}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var title = doc.GetTextBySelector("h1[itemprop=name]"); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(doc, url), Chapters = await FillChapters(url, bookId, GetToken(doc), title), Title = title, @@ -35,53 +34,63 @@ public override async Task<Book> Get(Uri url) { Annotation = doc.QuerySelector("div.description")?.InnerHtml, Seria = GetSeria(doc, url) }; - + return book; } - public override async Task Authorize() { - if (!Config.HasCredentials) { + public override async Task Authorize() + { + if (!Config.HasCredentials) + { return; } var doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri("/login")); var token = doc.QuerySelector("meta[name=_token]").Attributes["content"].Value; - + var response = await Config.Client.PostWithTriesAsync(SystemUrl.MakeRelativeUri("/login"), GetAuthData(token)); doc = await response.Content.ReadAsStreamAsync().ContinueWith(t => t.Result.AsHtmlDoc()); var helpBlock = doc.QuerySelector("input[type=email] + span.help-block"); - if (helpBlock == default) { + if (helpBlock == null) + { Config.Logger.LogInformation("Успешно авторизовались"); - } else { - throw new Exception($"Не удалось авторизоваться. {helpBlock.GetText()}"); + } + else + { + throw new Exception($"Не удалось авторизоваться. {helpBlock.GetText()}"); } } - - private FormUrlEncodedContent GetAuthData(string token) { - var data = new Dictionary<string, string> { - ["email"] = Config.Options.Login, - ["password"] = Config.Options.Password, - ["_token"] = token + + private FormUrlEncodedContent GetAuthData(string token) + { + var data = new Dictionary<string, string> + { + ["email"] = Config.Options.Login, ["password"] = Config.Options.Password, ["_token"] = token }; return new FormUrlEncodedContent(data); } - private static Author GetAuthor(HtmlDocument doc, Uri url) { + private static Author GetAuthor(HtmlDocument doc, Uri url) + { var a = doc.QuerySelector("h2[itemprop=author] a"); return new Author(a.GetText(), url.MakeRelativeUri(a.Attributes["href"].Value)); } - private async Task<IEnumerable<Chapter>> FillChapters(Uri uri, string bookId, string token, string title) { + private async Task<IEnumerable<Chapter>> FillChapters(Uri uri, string bookId, string token, string title) + { var result = new List<Chapter>(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - - foreach (var id in await GetToc(bookId)) { + + foreach (var id in await GetToc(bookId)) + { var chapter = new Chapter(); var content = await GetChapter(bookId, id, token); - if (content.StartsWith("{\"status\":\"error\"")) { + if (content.StartsWith("{\"status\":\"error\"")) + { Config.Logger.LogInformation($"Часть {id} заблокирована"); continue; } @@ -92,8 +101,7 @@ private async Task<IEnumerable<Chapter>> FillChapters(Uri uri, string bookId, st doc.RemoveNodes("h1"); chapter.Images = await GetImages(doc, uri); chapter.Content = doc.DocumentNode.InnerHtml; - - + Config.Logger.LogInformation($"Загружаю главу {chapter.Title.CoverQuotes()}"); result.Add(chapter); @@ -102,27 +110,33 @@ private async Task<IEnumerable<Chapter>> FillChapters(Uri uri, string bookId, st return result; } - private async Task<string> GetChapter(string bookId, string id, string token) { + private async Task<string> GetChapter(string bookId, string id, string token) + { var data = await Config.Client.PostWithTriesAsync(SystemUrl.MakeRelativeUri($"/reader/{bookId}/chapter"), GetData(id, token)); return await data.Content.ReadAsStringAsync(); } - - private static FormUrlEncodedContent GetData(string chapterId, string token) { - var data = new Dictionary<string, string> { - ["chapter"] = chapterId, - ["_token"] = token, + + private static FormUrlEncodedContent GetData(string chapterId, string token) + { + var data = new Dictionary<string, string> + { + ["chapter"] = chapterId, ["_token"] = token, }; return new FormUrlEncodedContent(data); } - private async Task<IEnumerable<string>> GetToc(string bookId) { + private async Task<IEnumerable<string>> GetToc(string bookId) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri($"/reader/{bookId}")); - var result = new Regex("capters: \\[(?<chapters>.*?)\\]").Match(doc.Text).Groups["chapters"].Value.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim('\"')).ToList(); + var result = new Regex("capters: \\[(?<chapters>.*?)\\]").Match(doc.Text).Groups["chapters"].Value.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim('\"')) + .ToList(); + return SliceToc(result, c => c); } - private static string GetToken(HtmlDocument doc) { + private static string GetToken(HtmlDocument doc) + { return doc.QuerySelector("meta[name=_token]").Attributes["content"].Value; } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/TwilightRussiaGetter.cs b/src/Core/Logic/Getters/TwilightRussiaGetter.cs similarity index 71% rename from Core/Logic/Getters/TwilightRussiaGetter.cs rename to src/Core/Logic/Getters/TwilightRussiaGetter.cs index b776382d..d0a43ba9 100644 --- a/Core/Logic/Getters/TwilightRussiaGetter.cs +++ b/src/Core/Logic/Getters/TwilightRussiaGetter.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; using System.Text; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -9,29 +6,31 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class TwilightRussiaGetter(BookGetterConfig config) : GetterBase(config) { +public class TwilightRussiaGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://twilightrussia.ru/"); - - public override async Task<Book> Get(Uri url) { + + public override async Task<Book> Get(Uri url) + { url = await GetMainUrl(url); url = SystemUrl.MakeRelativeUri($"/forum/{GetId(url)}"); - + var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { - Cover = null, - Chapters = await FillChapters(doc, url), - Title = doc.GetTextBySelector("a.forumBarA"), - Author = new Author(doc.GetTextBySelector("span[class^='forum_nik']")) + var book = new Book(url) + { + Cover = null, Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("a.forumBarA"), Author = new Author(doc.GetTextBySelector("span[class^='forum_nik']")) }; - + return book; } - private async Task<Uri> GetMainUrl(Uri url) { - if (url.ToString().Contains("/publ/")) { + private async Task<Uri> GetMainUrl(Uri url) + { + if (url.ToString().Contains("/publ/")) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); var a = doc.QuerySelector("#msgd ~ a[href]"); return a.Attributes["href"].Value.AsUri(); @@ -40,20 +39,24 @@ private async Task<Uri> GetMainUrl(Uri url) { return url; } - private async Task<IEnumerable<Chapter>> FillChapters(HtmlDocument doc, Uri url) { - if (Config.Options.NoChapters) { + private async Task<IEnumerable<Chapter>> FillChapters(HtmlDocument doc, Uri url) + { + if (Config.Options.NoChapters) + { return []; } - + var post = doc.QuerySelectorAll("td.content-block-forum"); - + var result = new List<Chapter>(); - foreach (var a in post.QuerySelectorAll("a[href*='/publ/']")) { + foreach (var a in post.QuerySelectorAll("a[href*='/publ/']")) + { var title = a.GetText(); - if (string.IsNullOrWhiteSpace(title)) { + if (string.IsNullOrWhiteSpace(title)) + { continue; } - + Config.Logger.LogInformation($"Загружаю главу {title}"); var href = url.MakeRelativeUri(a.Attributes["href"].Value); result.Add(await GetChapter(href, title)); @@ -61,27 +64,30 @@ private async Task<IEnumerable<Chapter>> FillChapters(HtmlDocument doc, Uri url) return result; } - - private async Task<Chapter> GetChapter(Uri url, string title) { + + private async Task<Chapter> GetChapter(Uri url, string title) + { var chapter = new Chapter(); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - + var text = new StringBuilder(); - foreach (var node in doc.QuerySelector("#msgd").ChildNodes) { + foreach (var node in doc.QuerySelector("#msgd").ChildNodes) + { var img = node.QuerySelector("img[src]"); - if (img != null) { + if (img != null) + { text.Append($"<img src='{img.Attributes["src"].Value}'/>"); continue; } - + var nodeText = node.GetText(); - if (!string.IsNullOrWhiteSpace(nodeText)) { + if (!string.IsNullOrWhiteSpace(nodeText)) + { text.Append(nodeText.CoverTag("p")); } } - - + var chapterDoc = text.AsHtmlDoc(); chapter.Images = await GetImages(chapterDoc, url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; @@ -89,4 +95,4 @@ private async Task<Chapter> GetChapter(Uri url, string title) { return chapter; } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/WattpadGetter.cs b/src/Core/Logic/Getters/WattpadGetter.cs similarity index 77% rename from Core/Logic/Getters/WattpadGetter.cs rename to src/Core/Logic/Getters/WattpadGetter.cs index dd2b2a4a..67a8b540 100644 --- a/Core/Logic/Getters/WattpadGetter.cs +++ b/src/Core/Logic/Getters/WattpadGetter.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; using System.Net.Http.Json; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -11,15 +8,19 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class WattpadGetter(BookGetterConfig config) : GetterBase(config) { +public class WattpadGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://wattpad.com/"); - protected override string GetId(Uri url) => base.GetId(url).Split('-')[0]; + protected override string GetId(Uri url) + => base.GetId(url).Split('-')[0]; - private async Task<string> GetStoryId(Uri url) { - if (url.ToString().Contains("/story/")) { + private async Task<string> GetStoryId(Uri url) + { + if (url.ToString().Contains("/story/")) + { return GetId(url); } @@ -27,32 +28,37 @@ private async Task<string> GetStoryId(Uri url) { return doc.QuerySelector("button[data-story-id]").Attributes["data-story-id"].Value; } - public override async Task<Book> Get(Uri url) { + public override async Task<Book> Get(Uri url) + { var storyId = await GetStoryId(url); url = SystemUrl.MakeRelativeUri(storyId); var wattpadInfo = await Config.Client.GetFromJsonAsync<WattpadInfo>(SystemUrl.MakeRelativeUri($"/api/v3/stories/{storyId}")); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(wattpadInfo), Chapters = await FillChapters(wattpadInfo), Title = wattpadInfo.Title, Author = new Author(wattpadInfo.User.Name, SystemUrl.MakeRelativeUri($"/user/{wattpadInfo.User.Name}")), Annotation = wattpadInfo.Description }; - + return book; } - private async Task<IEnumerable<Chapter>> FillChapters(WattpadInfo wattpadInfo) { + private async Task<IEnumerable<Chapter>> FillChapters(WattpadInfo wattpadInfo) + { var result = new List<Chapter>(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - - foreach (var group in SliceToc(wattpadInfo.Parts, c => c.FullName)) { + + foreach (var group in SliceToc(wattpadInfo.Parts, c => c.FullName)) + { Config.Logger.LogInformation($"Загружаю главу {group.FullName.CoverQuotes()}"); var chapter = new Chapter(); - + var chapterDoc = await GetChapter(group); chapter.Images = await GetImages(chapterDoc, group.Url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; @@ -64,16 +70,19 @@ private async Task<IEnumerable<Chapter>> FillChapters(WattpadInfo wattpadInfo) { return result; } - private async Task<HtmlDocument> GetChapter(WattpadPart part) { + private async Task<HtmlDocument> GetChapter(WattpadPart part) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(part.Url); - foreach (var node in doc.QuerySelectorAll("p")) { + foreach (var node in doc.QuerySelectorAll("p")) + { node.Attributes.RemoveAll(); } - + return doc; } - private Task<TempFile> GetCover(WattpadInfo wattpadInfo) { + private Task<TempFile> GetCover(WattpadInfo wattpadInfo) + { return !string.IsNullOrWhiteSpace(wattpadInfo.Cover) ? SaveImage(wattpadInfo.Cover.AsUri()) : Task.FromResult(default(TempFile)); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/WuxiaWorldGetter.cs b/src/Core/Logic/Getters/WuxiaWorldGetter.cs similarity index 71% rename from Core/Logic/Getters/WuxiaWorldGetter.cs rename to src/Core/Logic/Getters/WuxiaWorldGetter.cs index dd4b7e57..b27a646f 100644 --- a/Core/Logic/Getters/WuxiaWorldGetter.cs +++ b/src/Core/Logic/Getters/WuxiaWorldGetter.cs @@ -1,11 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Net.Http; using System.Net.Http.Json; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -15,44 +10,49 @@ using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Core.Logic.Getters; -public class WuxiaWorldGetter(BookGetterConfig config) : GetterBase(config) { +public class WuxiaWorldGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://wuxiaworld.ru/"); - - private async Task<Uri> GetMainUrl(Uri url) { - if (url.GetSegment(1) == "category") { + + private async Task<Uri> GetMainUrl(Uri url) + { + if (url.GetSegment(1) == "category") + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); url = doc.QuerySelector("h2[itemprop=name] a").Attributes["href"].Value.AsUri(); } return url; } - - public override async Task<Book> Get(Uri url) { + + public override async Task<Book> Get(Uri url) + { url = await GetMainUrl(url); var doc = await GetSafety(url); - var book = new Book(url) { - Cover = await GetCover(doc, url), - Chapters = await FillChapters(doc, url), - Title = doc.GetTextBySelector("h1"), - Author = new Author("WuxiaWorld"), + var book = new Book(url) + { + Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("h1"), Author = new Author("WuxiaWorld"), }; - + return book; } - private async Task<IEnumerable<UrlChapter>> GetToc(HtmlDocument doc, Uri url) { + private async Task<IEnumerable<UrlChapter>> GetToc(HtmlDocument doc, Uri url) + { var catId = Regex.Match(doc.ParsedText, "data-cat=\"(?<catId>\\d+)\"").Groups["catId"].Value; var result = new List<UrlChapter>(); - - foreach (var span in doc.QuerySelectorAll("ul.myUL span.caret[data-id]")) { + + foreach (var span in doc.QuerySelectorAll("ul.myUL span.caret[data-id]")) + { var offset = (int.Parse(span.Attributes["data-id"].Value) - 1) * 100; - var payload = new FormUrlEncodedContent(new Dictionary<string, string> { - ["cat_id"] = catId, - ["offset"] = offset.ToString() - }); + var payload = new FormUrlEncodedContent( + new Dictionary<string, string> + { + ["cat_id"] = catId, ["offset"] = offset.ToString() + }); var post = await Config.Client.PostWithTriesAsync(SystemUrl.MakeRelativeUri("/wp-content/themes/Wuxia/template-parts/post/menu-query.php"), payload); var toc = await post.Content.ReadFromJsonAsync<WuxiaWorldToc[]>(); @@ -62,16 +62,19 @@ private async Task<IEnumerable<UrlChapter>> GetToc(HtmlDocument doc, Uri url) { return SliceToc(result, c => c.Title); } - private async Task<IEnumerable<Chapter>> FillChapters(HtmlDocument doc, Uri url) { + private async Task<IEnumerable<Chapter>> FillChapters(HtmlDocument doc, Uri url) + { var result = new List<Chapter>(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var bookChapter in await GetToc(doc, url)) { + foreach (var bookChapter in await GetToc(doc, url)) + { var chapter = new Chapter(); Config.Logger.LogInformation($"Загружаю главу {bookChapter.Title.CoverQuotes()}"); - + var chapterDoc = await GetChapter(bookChapter.Url); chapter.Title = bookChapter.Title; chapter.Images = await GetImages(chapterDoc, url); @@ -83,27 +86,31 @@ private async Task<IEnumerable<Chapter>> FillChapters(HtmlDocument doc, Uri url) return result; } - private async Task<HtmlDocument> GetChapter(Uri url) { + private async Task<HtmlDocument> GetChapter(Uri url) + { var doc = await GetSafety(url); return doc.QuerySelector("div.js-full-content").RemoveNodes("> :not(p)").InnerHtml.AsHtmlDoc(); } - private Task<TempFile> GetCover(HtmlDocument doc, Uri uri) { + private Task<TempFile> GetCover(HtmlDocument doc, Uri uri) + { var imagePath = doc.QuerySelector("meta[property=og:image]")?.Attributes["content"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } - - private async Task<HtmlDocument> GetSafety(Uri url) { + + private async Task<HtmlDocument> GetSafety(Uri url) + { var response = await Config.Client.GetWithTriesAsync(url); await Task.Delay(TimeSpan.FromSeconds(1)); - - while (response == default || response.StatusCode == HttpStatusCode.ServiceUnavailable) { + + while (response == null || response.StatusCode == HttpStatusCode.ServiceUnavailable) + { Config.Logger.LogInformation("Получен бан от системы. Жду..."); var errorTimeout = TimeSpan.FromSeconds(30); response = await Config.Client.GetWithTriesAsync(url, errorTimeout); await Task.Delay(errorTimeout); } - + return await response.Content.ReadAsStringAsync().ContinueWith(t => t.Result.AsHtmlDoc()); } -} \ No newline at end of file +} diff --git a/Core/Logic/Getters/YounettranslateGetter.cs b/src/Core/Logic/Getters/YounettranslateGetter.cs similarity index 75% rename from Core/Logic/Getters/YounettranslateGetter.cs rename to src/Core/Logic/Getters/YounettranslateGetter.cs index d7d640ae..c02a4184 100644 --- a/Core/Logic/Getters/YounettranslateGetter.cs +++ b/src/Core/Logic/Getters/YounettranslateGetter.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Core.Configs; using Core.Extensions; using Core.Types.Book; @@ -12,18 +8,22 @@ namespace Core.Logic.Getters; -public class YounettranslateGetter(BookGetterConfig config) : GetterBase(config) { +public class YounettranslateGetter(BookGetterConfig config) : GetterBase(config) +{ protected override Uri SystemUrl => new("https://younettranslate.com/"); - protected override string GetId(Uri url) { + protected override string GetId(Uri url) + { return url.GetSegment(2); } - public override async Task<Book> Get(Uri url) { + public override async Task<Book> Get(Uri url) + { url = SystemUrl.MakeRelativeUri($"/projects/{GetId(url)}"); var doc = await Config.Client.GetHtmlDocWithTriesAsync(url); - var book = new Book(url) { + var book = new Book(url) + { Cover = await GetCover(url), Chapters = await FillChapters(url), Title = GetTitle(doc), @@ -34,10 +34,13 @@ public override async Task<Book> Get(Uri url) { return book; } - private Author GetAuthor(HtmlDocument doc) { - foreach (var div in doc.QuerySelectorAll("div.col-md-3")) { + private Author GetAuthor(HtmlDocument doc) + { + foreach (var div in doc.QuerySelectorAll("div.col-md-3")) + { var label = div.QuerySelector("p.small-label"); - if (label != default && label.GetText().StartsWith("Автор")) { + if (label != null && label.GetText().StartsWith("Автор")) + { return new Author(label.GetTextBySelector("+ p")); } } @@ -45,25 +48,31 @@ private Author GetAuthor(HtmlDocument doc) { return new Author("Younettranslate"); } - private static string GetTitle(HtmlDocument doc) { + private static string GetTitle(HtmlDocument doc) + { return doc.GetTextBySelector("h1"); } - private async Task<IEnumerable<Chapter>> FillChapters(Uri url) { + private async Task<IEnumerable<Chapter>> FillChapters(Uri url) + { var result = new List<Chapter>(); - if (Config.Options.NoChapters) { + if (Config.Options.NoChapters) + { return result; } - foreach (var urlChapter in await GetToc(url)) { + foreach (var urlChapter in await GetToc(url)) + { Config.Logger.LogInformation($"Загружаю главу {urlChapter.Title.CoverQuotes()}"); - var chapter = new Chapter { + var chapter = new Chapter + { Title = urlChapter.Title }; var chapterDoc = await GetChapter(urlChapter); - if (chapterDoc != default) { + if (chapterDoc != null) + { chapter.Images = await GetImages(chapterDoc, SystemUrl); chapter.Content = chapterDoc.DocumentNode.InnerHtml; } @@ -74,31 +83,36 @@ private async Task<IEnumerable<Chapter>> FillChapters(Uri url) { return result; } - private async Task<IEnumerable<UrlChapter>> GetToc(Uri url) { + private async Task<IEnumerable<UrlChapter>> GetToc(Uri url) + { var result = new List<UrlChapter>(); - for (var i = 1;; i++) { + for (var i = 1;; i++) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(url.AppendQueryParameter("page", i)); var links = doc.QuerySelectorAll("table.catposts-table td.hidden-sm a[href]").Select(a => new UrlChapter(url.MakeRelativeUri(a.Attributes["href"].Value), a.GetText())).ToList(); - if (links.Count == 0) { + if (links.Count == 0) + { break; } - + result.AddRange(links); } return SliceToc(result, c => c.Title); } - private async Task<HtmlDocument> GetChapter(UrlChapter urlChapter) { + private async Task<HtmlDocument> GetChapter(UrlChapter urlChapter) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync(urlChapter.Url); return doc.QuerySelector("div.postdata-content").RemoveNodes(n => n.InnerText.Contains("ЗАПРЕЩЕНО ПУБЛИКОВАТЬ")).InnerHtml.AsHtmlDoc(); } - private async Task<TempFile> GetCover(Uri bookUri) { + private async Task<TempFile> GetCover(Uri bookUri) + { var doc = await Config.Client.GetHtmlDocWithTriesAsync("https://younettranslate.com/projects/".AsUri()); - + var imagePath = doc.QuerySelector($"div.category-item a[href='{bookUri}'] img")?.Attributes["src"]?.Value; - return !string.IsNullOrWhiteSpace(imagePath) ? await SaveImage(bookUri.MakeRelativeUri(imagePath)) : default; + return !string.IsNullOrWhiteSpace(imagePath) ? await SaveImage(bookUri.MakeRelativeUri(imagePath)) : null; } -} \ No newline at end of file +} diff --git a/Core/Misc/AdditionalTypeEnum.cs b/src/Core/Misc/AdditionalTypeEnum.cs similarity index 60% rename from Core/Misc/AdditionalTypeEnum.cs rename to src/Core/Misc/AdditionalTypeEnum.cs index eb39e763..ca1a322d 100644 --- a/Core/Misc/AdditionalTypeEnum.cs +++ b/src/Core/Misc/AdditionalTypeEnum.cs @@ -1,10 +1,9 @@ -using System; - namespace Core.Misc; [Flags] -public enum AdditionalTypeEnum { +public enum AdditionalTypeEnum +{ Books = 1, Audio = 2, Images = 4 -} \ No newline at end of file +} diff --git a/Core/Misc/BuilderProvider.cs b/src/Core/Misc/BuilderProvider.cs similarity index 83% rename from Core/Misc/BuilderProvider.cs rename to src/Core/Misc/BuilderProvider.cs index 1ac97517..0db5762b 100644 --- a/Core/Misc/BuilderProvider.cs +++ b/src/Core/Misc/BuilderProvider.cs @@ -1,13 +1,15 @@ -using System; using Core.Configs; using Core.Logic.Builders; using Microsoft.Extensions.Logging; namespace Core.Misc; -public static class BuilderProvider { - public static BuilderBase Get(string format, Options options, ILogger logger) { - return format.Trim().ToLower() switch { +public static class BuilderProvider +{ + public static BuilderBase Get(string format, Options options, ILogger logger) + { + return format.Trim().ToLower() switch + { "fb2" => new Fb2Builder(options, logger), "epub" => new EpubBuilder(options, logger), "json" => new JsonBuilder(options, logger), @@ -17,4 +19,4 @@ public static BuilderBase Get(string format, Options options, ILogger logger) { _ => throw new ArgumentException("Неизвестный формат", nameof(format)) }; } -} \ No newline at end of file +} diff --git a/Core/Misc/GetterProvider.cs b/src/Core/Misc/GetterProvider.cs similarity index 75% rename from Core/Misc/GetterProvider.cs rename to src/Core/Misc/GetterProvider.cs index 5bd6610c..e3ad1060 100644 --- a/Core/Misc/GetterProvider.cs +++ b/src/Core/Misc/GetterProvider.cs @@ -1,17 +1,17 @@ -using System; -using System.Linq; using System.Reflection; using Core.Configs; using Core.Logic.Getters; namespace Core.Misc; -public static class GetterProvider { - public static GetterBase Get(BookGetterConfig config, Uri url) { +public static class GetterProvider +{ + public static GetterBase Get(BookGetterConfig config, Uri url) + { return Assembly.GetAssembly(typeof(GetterBase))!.GetTypes() .Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(GetterBase))) - .Select(type => (GetterBase) Activator.CreateInstance(type, config)) + .Select(type => (GetterBase)Activator.CreateInstance(type, config)) .FirstOrDefault(g => g!.IsSameUrl(url)) ?? throw new ArgumentException($"Сайт {url.Host} не поддерживается"); } -} \ No newline at end of file +} diff --git a/Core/Misc/TempFolder/TempFolder.cs b/src/Core/Misc/TempFolder/TempFolder.cs similarity index 65% rename from Core/Misc/TempFolder/TempFolder.cs rename to src/Core/Misc/TempFolder/TempFolder.cs index 788515fd..a93f4bfd 100644 --- a/Core/Misc/TempFolder/TempFolder.cs +++ b/src/Core/Misc/TempFolder/TempFolder.cs @@ -1,10 +1,7 @@ -using System; -using System.IO; -using System.Linq; +namespace Core.Misc.TempFolder; -namespace Core.Misc.TempFolder; - -public class TempFolder : IDisposable { +public class TempFolder : IDisposable +{ /// <summary> /// Temporary's folder path /// </summary> @@ -14,13 +11,16 @@ public class TempFolder : IDisposable { /// Create new instance of temporary folder that will be deleted after dispose /// </summary> /// <param name="path">Temporary folder</param> - public TempFolder(string path) { + public TempFolder(string path) + { Path = path; } - - public void Dispose() { - if (Directory.Exists(Path) && !Directory.EnumerateFileSystemEntries(Path).Any()) { + + public void Dispose() + { + if (Directory.Exists(Path) && !Directory.EnumerateFileSystemEntries(Path).Any()) + { Directory.Delete(Path, true); } } -} \ No newline at end of file +} diff --git a/Core/Misc/TempFolder/TempFolderFactory.cs b/src/Core/Misc/TempFolder/TempFolderFactory.cs similarity index 69% rename from Core/Misc/TempFolder/TempFolderFactory.cs rename to src/Core/Misc/TempFolder/TempFolderFactory.cs index b1e85fc6..b36f7eac 100644 --- a/Core/Misc/TempFolder/TempFolderFactory.cs +++ b/src/Core/Misc/TempFolder/TempFolderFactory.cs @@ -1,20 +1,21 @@ #nullable enable -using System.IO; +namespace Core.Misc.TempFolder; -namespace Core.Misc.TempFolder; - -public class TempFolderFactory { +public class TempFolderFactory +{ /// <summary> /// Create temporary folder by path /// </summary> /// <param name="path">Temporary folder path</param> /// <returns>New instance of temporary folder</returns> - public static TempFolder Create(string? path) { + public static TempFolder Create(string? path) + { path ??= Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), $"{Path.GetRandomFileName()}_e2e_temp")).FullName; - if (!Directory.Exists(path)) { + if (!Directory.Exists(path)) + { Directory.CreateDirectory(path); } - + return new TempFolder(path); } -} \ No newline at end of file +} diff --git a/Core/Patterns/ChapterPattern.xhtml b/src/Core/Patterns/ChapterPattern.xhtml similarity index 59% rename from Core/Patterns/ChapterPattern.xhtml rename to src/Core/Patterns/ChapterPattern.xhtml index 8ef10c4e..df1a5967 100644 --- a/Core/Patterns/ChapterPattern.xhtml +++ b/src/Core/Patterns/ChapterPattern.xhtml @@ -1,14 +1,14 @@ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html - PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>{title} - + -

{title}

- {body} +

{title}

+{body} - \ No newline at end of file + diff --git a/Core/Patterns/Roboto-Italic.ttf b/src/Core/Patterns/Roboto-Italic.ttf similarity index 100% rename from Core/Patterns/Roboto-Italic.ttf rename to src/Core/Patterns/Roboto-Italic.ttf diff --git a/Core/Patterns/Roboto-Regular.ttf b/src/Core/Patterns/Roboto-Regular.ttf similarity index 100% rename from Core/Patterns/Roboto-Regular.ttf rename to src/Core/Patterns/Roboto-Regular.ttf diff --git a/Core/Patterns/stylesheet.css b/src/Core/Patterns/stylesheet.css similarity index 68% rename from Core/Patterns/stylesheet.css rename to src/Core/Patterns/stylesheet.css index 466efc6b..b1b699e3 100644 --- a/Core/Patterns/stylesheet.css +++ b/src/Core/Patterns/stylesheet.css @@ -1,16 +1,16 @@ @font-face { - font-family: Roboto; - font-weight: normal; - font-style: normal; - src: url(Roboto-Regular.ttf); + font-family: Roboto; + font-weight: normal; + font-style: normal; + src: url(Roboto-Regular.ttf); } @font-face { - font-family: Roboto-Italic !important; - font-weight: normal; - font-style: italic; - src: url(Roboto-Italic.ttf); + font-family: Roboto-Italic !important; + font-weight: normal; + font-style: italic; + src: url(Roboto-Italic.ttf); } body { @@ -45,4 +45,4 @@ img { display: block; margin-left: auto; margin-right: auto; -} \ No newline at end of file +} diff --git a/src/Core/Types/AuthorToday/AuthorTodayAuthResponse.cs b/src/Core/Types/AuthorToday/AuthorTodayAuthResponse.cs new file mode 100644 index 00000000..5be99357 --- /dev/null +++ b/src/Core/Types/AuthorToday/AuthorTodayAuthResponse.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.AuthorToday; + +public class AuthorTodayAuthResponse +{ + [JsonPropertyName("message")] public string Message { get; set; } + + [JsonPropertyName("token")] public string Token { get; set; } +} diff --git a/src/Core/Types/AuthorToday/AuthorTodayBookDetails.cs b/src/Core/Types/AuthorToday/AuthorTodayBookDetails.cs new file mode 100644 index 00000000..03b27897 --- /dev/null +++ b/src/Core/Types/AuthorToday/AuthorTodayBookDetails.cs @@ -0,0 +1,32 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.AuthorToday; + +public class AuthorTodayBookDetails +{ + [JsonPropertyName("annotation")] public string Annotation { get; set; } + + [JsonPropertyName("seriesId")] public long? SeriesId { get; set; } + + [JsonPropertyName("seriesWorkNumber")] public long? SeriesWorkNumber { get; set; } + + [JsonPropertyName("seriesTitle")] public string SeriesTitle { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("authorFIO")] public string AuthorFio { get; set; } + + [JsonPropertyName("coAuthorFIO")] public string CoAuthorFio { get; set; } + + [JsonPropertyName("coverUrl")] public string CoverUrl { get; set; } + + [JsonPropertyName("chapters")] public AuthorTodayChapter[] Chapters { get; set; } + + [JsonPropertyName("id")] public long Id { get; set; } + + [JsonPropertyName("authorUserName")] public string AuthorUserName { get; set; } + + [JsonPropertyName("CoAuthorUserName")] public string CoAuthorUserName { get; set; } + + [JsonPropertyName("galleryImages")] public AuthorTodayImage[] GalleryImages { get; set; } +} diff --git a/src/Core/Types/AuthorToday/AuthorTodayChapter.cs b/src/Core/Types/AuthorToday/AuthorTodayChapter.cs new file mode 100644 index 00000000..b0a3ff0d --- /dev/null +++ b/src/Core/Types/AuthorToday/AuthorTodayChapter.cs @@ -0,0 +1,46 @@ +using System.Security.Cryptography; +using System.Text; +using System.Text.Json.Serialization; + +namespace Core.Types.AuthorToday; + +public class AuthorTodayChapter +{ + [JsonPropertyName("id")] public long Id { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("sortOrder")] public long SortOrder { get; set; } + + [JsonPropertyName("text")] public string Text { get; set; } + + [JsonPropertyName("key")] public string Key { get; set; } + + [JsonPropertyName("IsSuccessful")] public bool IsSuccessful { get; set; } + + [JsonPropertyName("IsDraft")] public bool IsDraft { get; set; } + + [JsonPropertyName("code")] public string Code { get; set; } + + public string Decode(string userId, string cert) + { + var secret = $"{string.Concat(Key.Reverse())}:{(string.IsNullOrWhiteSpace(userId) ? "Guest" : userId)}:{"FjPg]{2+$8JRvv~("}:{cert}"; + var hashSecret = Convert.ToHexString(MD5.HashData(Encoding.UTF8.GetBytes(secret))); + + using var aes = Aes.Create(); + const int IV_SHIFT = 16; + + var aesKey = Encoding.UTF8.GetBytes(hashSecret)[..IV_SHIFT]; + + aes.Key = aesKey; + aes.IV = aesKey; + + using var ms = new MemoryStream(Convert.FromBase64String(Text)); + using var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read); + + var output = new MemoryStream(); + cs.CopyTo(output); + + return Encoding.UTF8.GetString(output.ToArray()); + } +} diff --git a/src/Core/Types/AuthorToday/AuthorTodayImage.cs b/src/Core/Types/AuthorToday/AuthorTodayImage.cs new file mode 100644 index 00000000..a2c27ece --- /dev/null +++ b/src/Core/Types/AuthorToday/AuthorTodayImage.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.AuthorToday; + +public class AuthorTodayImage +{ + [JsonPropertyName("url")] public string Url { get; set; } +} diff --git a/src/Core/Types/AuthorToday/AuthorTodayUser.cs b/src/Core/Types/AuthorToday/AuthorTodayUser.cs new file mode 100644 index 00000000..0c88c898 --- /dev/null +++ b/src/Core/Types/AuthorToday/AuthorTodayUser.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.AuthorToday; + +public class AuthorTodayUser +{ + [JsonPropertyName("id")] public long Id { get; set; } +} diff --git a/Core/Types/Book/AdditionalFileCollection.cs b/src/Core/Types/Book/AdditionalFileCollection.cs similarity index 58% rename from Core/Types/Book/AdditionalFileCollection.cs rename to src/Core/Types/Book/AdditionalFileCollection.cs index 5c2b64c8..931b73c4 100644 --- a/Core/Types/Book/AdditionalFileCollection.cs +++ b/src/Core/Types/Book/AdditionalFileCollection.cs @@ -1,36 +1,41 @@ -using System; -using System.Collections.Generic; -using System.Linq; using Core.Misc; using Core.Types.Common; namespace Core.Types.Book; -public class AdditionalFileCollection : IDisposable { +public class AdditionalFileCollection : IDisposable +{ public Dictionary> Collection { get; set; } = new(); - public void Add(AdditionalTypeEnum type, TempFile file) { - if (!Collection.TryGetValue(type, out var files)) { + public void Add(AdditionalTypeEnum type, TempFile file) + { + if (!Collection.TryGetValue(type, out var files)) + { files = new(); Collection[type] = files; } - + files.Add(file); } - - public void Add(AdditionalTypeEnum type, IEnumerable files) { - foreach (var file in files) { + + public void Add(AdditionalTypeEnum type, IEnumerable files) + { + foreach (var file in files) + { Add(type, file); } } - public List Get(AdditionalTypeEnum type) { + public List Get(AdditionalTypeEnum type) + { return Collection.TryGetValue(type, out var result) ? result : []; } - public void Dispose() { - foreach (var file in Collection.SelectMany(pair => pair.Value)) { + public void Dispose() + { + foreach (var file in Collection.SelectMany(pair => pair.Value)) + { file.Dispose(); } } -} \ No newline at end of file +} diff --git a/Core/Types/Book/Author.cs b/src/Core/Types/Book/Author.cs similarity index 59% rename from Core/Types/Book/Author.cs rename to src/Core/Types/Book/Author.cs index 5f2bf87f..685723c7 100644 --- a/Core/Types/Book/Author.cs +++ b/src/Core/Types/Book/Author.cs @@ -1,20 +1,20 @@ -using System; +namespace Core.Types.Book; -namespace Core.Types.Book; - -public class Author { +public class Author +{ /// /// Имя автора /// - public string Name { get; set; } + public string Name { get; } /// /// Url профиля автора /// public Uri Url { get; set; } - public Author(string name, Uri url = null) { + public Author(string name, Uri url = null) + { Name = name; Url = url; } -} \ No newline at end of file +} diff --git a/Core/Types/Book/Book.cs b/src/Core/Types/Book/Book.cs similarity index 87% rename from Core/Types/Book/Book.cs rename to src/Core/Types/Book/Book.cs index 02603f64..ed913718 100644 --- a/Core/Types/Book/Book.cs +++ b/src/Core/Types/Book/Book.cs @@ -1,15 +1,14 @@ -using System; -using System.Collections.Generic; using Core.Types.Common; -namespace Core.Types.Book; +namespace Core.Types.Book; -public class Book : IDisposable { +public class Book : IDisposable +{ /// /// Название книги /// public string Title { get; set; } - + /// /// Автор книги /// @@ -24,12 +23,12 @@ public class Book : IDisposable { /// Описание книги /// public string Annotation { get; set; } - + /// /// Обложка /// public TempFile Cover { get; set; } - + /// /// Серия /// @@ -55,12 +54,14 @@ public class Book : IDisposable { /// public string Lang { get; set; } = "ru"; - public Book(Uri url) { + public Book(Uri url) + { Url = url; } - public void Dispose() { + public void Dispose() + { Cover?.Dispose(); AdditionalFiles?.Dispose(); } -} \ No newline at end of file +} diff --git a/Core/Types/Book/Chapter.cs b/src/Core/Types/Book/Chapter.cs similarity index 85% rename from Core/Types/Book/Chapter.cs rename to src/Core/Types/Book/Chapter.cs index cbdab9ab..0cc07bb4 100644 --- a/Core/Types/Book/Chapter.cs +++ b/src/Core/Types/Book/Chapter.cs @@ -1,14 +1,14 @@ -using System.Collections.Generic; using Core.Types.Common; -namespace Core.Types.Book; +namespace Core.Types.Book; -public class Chapter { +public class Chapter +{ /// /// Название части /// public string Title { get; set; } - + /// /// Контент части /// @@ -23,4 +23,4 @@ public class Chapter { /// Валидна ли часть /// public bool IsValid => !string.IsNullOrWhiteSpace(Content); -} \ No newline at end of file +} diff --git a/Core/Types/Book/Seria.cs b/src/Core/Types/Book/Seria.cs similarity index 82% rename from Core/Types/Book/Seria.cs rename to src/Core/Types/Book/Seria.cs index dbb23f30..969f2045 100644 --- a/Core/Types/Book/Seria.cs +++ b/src/Core/Types/Book/Seria.cs @@ -1,20 +1,19 @@ -using System; +namespace Core.Types.Book; -namespace Core.Types.Book; - -public class Seria { +public class Seria +{ /// /// Название серии /// public string Name { get; set; } - + /// /// Номер в серии /// public string Number { get; set; } - + /// /// Ссылка на книги серии /// public Uri Url { get; set; } -} \ No newline at end of file +} diff --git a/src/Core/Types/BookYandex/BooksYandexAudioBitrate.cs b/src/Core/Types/BookYandex/BooksYandexAudioBitrate.cs new file mode 100644 index 00000000..0dd88efc --- /dev/null +++ b/src/Core/Types/BookYandex/BooksYandexAudioBitrate.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.BookYandex; + +public class BooksYandexAudioBitrate +{ + [JsonPropertyName("url")] public string Url { get; set; } +} diff --git a/src/Core/Types/BookYandex/BooksYandexAudioOffline.cs b/src/Core/Types/BookYandex/BooksYandexAudioOffline.cs new file mode 100644 index 00000000..1d08394b --- /dev/null +++ b/src/Core/Types/BookYandex/BooksYandexAudioOffline.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.BookYandex; + +public class BooksYandexAudioOffline +{ + [JsonPropertyName("max_bit_rate")] public BooksYandexAudioBitrate Max { get; set; } + + [JsonPropertyName("min_bit_rate")] public BooksYandexAudioBitrate Min { get; set; } +} diff --git a/src/Core/Types/BookYandex/BooksYandexAudioTrack.cs b/src/Core/Types/BookYandex/BooksYandexAudioTrack.cs new file mode 100644 index 00000000..9c3bf6de --- /dev/null +++ b/src/Core/Types/BookYandex/BooksYandexAudioTrack.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.BookYandex; + +public class BooksYandexAudioTrack +{ + [JsonPropertyName("offline")] public BooksYandexAudioOffline Offline { get; set; } +} diff --git a/src/Core/Types/BookYandex/BooksYandexAuthor.cs b/src/Core/Types/BookYandex/BooksYandexAuthor.cs new file mode 100644 index 00000000..fdb745d9 --- /dev/null +++ b/src/Core/Types/BookYandex/BooksYandexAuthor.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.BookYandex; + +public class BooksYandexAuthor +{ + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("uuid")] public string Uuid { get; set; } +} diff --git a/src/Core/Types/BookYandex/BooksYandexBook.cs b/src/Core/Types/BookYandex/BooksYandexBook.cs new file mode 100644 index 00000000..05301be4 --- /dev/null +++ b/src/Core/Types/BookYandex/BooksYandexBook.cs @@ -0,0 +1,44 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.BookYandex; + +public abstract class BookmateBookBase +{ + [JsonPropertyName("uuid")] public string UUID { get; set; } + + [JsonPropertyName("annotation")] public string Annotation { get; set; } + + [JsonPropertyName("cover")] public BooksYandexCover Cover { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("language")] public string Language { get; set; } + + [JsonPropertyName("linked_book_uuids")] public string[] LinkedBooks { get; set; } + + [JsonPropertyName("linked_audiobook_uuids")] public string[] LinkedAudio { get; set; } + + [JsonPropertyName("source_type")] public string SourceType { get; set; } + + public abstract BooksYandexAuthor GetAuthor(); +} + +public class BooksYandexBook : BookmateBookBase +{ + [JsonPropertyName("authors_objects")] public BooksYandexAuthor[] AuthorsObjects { get; set; } + + public override BooksYandexAuthor GetAuthor() + { + return AuthorsObjects?.FirstOrDefault(); + } +} + +public class BookmateComic : BookmateBookBase +{ + [JsonPropertyName("authors")] public BooksYandexAuthor[] Authors { get; set; } + + public override BooksYandexAuthor GetAuthor() + { + return Authors?.FirstOrDefault(); + } +} diff --git a/src/Core/Types/BookYandex/BooksYandexComicContent.cs b/src/Core/Types/BookYandex/BooksYandexComicContent.cs new file mode 100644 index 00000000..cfb5126d --- /dev/null +++ b/src/Core/Types/BookYandex/BooksYandexComicContent.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.BookYandex; + +public class BooksYandexComicContent +{ + [JsonPropertyName("uris")] public BooksYandexUri Uri { get; set; } +} diff --git a/src/Core/Types/BookYandex/BooksYandexComicMetadata.cs b/src/Core/Types/BookYandex/BooksYandexComicMetadata.cs new file mode 100644 index 00000000..ca5ee961 --- /dev/null +++ b/src/Core/Types/BookYandex/BooksYandexComicMetadata.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.BookYandex; + +public class BooksYandexComicMetadata +{ + [JsonPropertyName("pages")] public BooksYandexComicPage[] Pages { get; set; } +} diff --git a/src/Core/Types/BookYandex/BooksYandexComicPage.cs b/src/Core/Types/BookYandex/BooksYandexComicPage.cs new file mode 100644 index 00000000..797c0c78 --- /dev/null +++ b/src/Core/Types/BookYandex/BooksYandexComicPage.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.BookYandex; + +public class BooksYandexComicPage +{ + [JsonPropertyName("content")] public BooksYandexComicContent Content { get; set; } +} diff --git a/src/Core/Types/BookYandex/BooksYandexCover.cs b/src/Core/Types/BookYandex/BooksYandexCover.cs new file mode 100644 index 00000000..5cf57455 --- /dev/null +++ b/src/Core/Types/BookYandex/BooksYandexCover.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.BookYandex; + +public class BooksYandexCover +{ + [JsonPropertyName("large")] public string Large { get; set; } + + [JsonPropertyName("small")] public string Small { get; set; } +} diff --git a/src/Core/Types/BookYandex/BooksYandexEpisode.cs b/src/Core/Types/BookYandex/BooksYandexEpisode.cs new file mode 100644 index 00000000..e56ff4d3 --- /dev/null +++ b/src/Core/Types/BookYandex/BooksYandexEpisode.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.BookYandex; + +public class BooksYandexEpisode +{ + [JsonPropertyName("uuid")] public string Uuid { get; set; } + + [JsonPropertyName("can_be_read")] public bool CanBeRead { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } +} diff --git a/src/Core/Types/BookYandex/BooksYandexEpisodes.cs b/src/Core/Types/BookYandex/BooksYandexEpisodes.cs new file mode 100644 index 00000000..5457bbeb --- /dev/null +++ b/src/Core/Types/BookYandex/BooksYandexEpisodes.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.BookYandex; + +public class BooksYandexEpisodes +{ + [JsonPropertyName("episodes")] public BooksYandexEpisode[] Episodes { get; set; } +} diff --git a/src/Core/Types/BookYandex/BooksYandexPlaylist.cs b/src/Core/Types/BookYandex/BooksYandexPlaylist.cs new file mode 100644 index 00000000..b8c1416c --- /dev/null +++ b/src/Core/Types/BookYandex/BooksYandexPlaylist.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.BookYandex; + +public class BooksYandexPlaylist +{ + [JsonPropertyName("tracks")] public BooksYandexAudioTrack[] Tracks { get; set; } +} diff --git a/src/Core/Types/BookYandex/BooksYandexResponse.cs b/src/Core/Types/BookYandex/BooksYandexResponse.cs new file mode 100644 index 00000000..4e0f14b3 --- /dev/null +++ b/src/Core/Types/BookYandex/BooksYandexResponse.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.BookYandex; + +public class BooksYandexResponse +{ + [JsonPropertyName("book")] public BooksYandexBook Book { get; set; } + + [JsonPropertyName("audiobook")] public BooksYandexBook AudioBook { get; set; } + + [JsonPropertyName("comicbook")] public BookmateComic Comicbook { get; set; } +} diff --git a/src/Core/Types/BookYandex/BooksYandexUri.cs b/src/Core/Types/BookYandex/BooksYandexUri.cs new file mode 100644 index 00000000..5ca243d3 --- /dev/null +++ b/src/Core/Types/BookYandex/BooksYandexUri.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.BookYandex; + +public class BooksYandexUri +{ + [JsonPropertyName("Image")] public string Image { get; set; } + + [JsonPropertyName("path")] public string Path { get; set; } +} diff --git a/src/Core/Types/Bookriver/BookRiverApiResponse.cs b/src/Core/Types/Bookriver/BookRiverApiResponse.cs new file mode 100644 index 00000000..7ab44b25 --- /dev/null +++ b/src/Core/Types/Bookriver/BookRiverApiResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Bookriver; + +public class BookRiverApiResponse +{ + [JsonPropertyName("data")] public T Data { get; set; } +} diff --git a/src/Core/Types/Bookriver/BookRiverAuthResponse.cs b/src/Core/Types/Bookriver/BookRiverAuthResponse.cs new file mode 100644 index 00000000..3e0b02e7 --- /dev/null +++ b/src/Core/Types/Bookriver/BookRiverAuthResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Bookriver; + +public class BookRiverAuthResponse +{ + [JsonPropertyName("token")] public string Token { get; set; } +} diff --git a/src/Core/Types/Bookriver/BookRiverBook.cs b/src/Core/Types/Bookriver/BookRiverBook.cs new file mode 100644 index 00000000..1c01f510 --- /dev/null +++ b/src/Core/Types/Bookriver/BookRiverBook.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Bookriver; + +public class BookRiverBook +{ + [JsonPropertyName("currentGraphqlBook")] public BookRiverCurrentBook Book { get; set; } +} diff --git a/src/Core/Types/Bookriver/BookRiverChapter.cs b/src/Core/Types/Bookriver/BookRiverChapter.cs new file mode 100644 index 00000000..735617ff --- /dev/null +++ b/src/Core/Types/Bookriver/BookRiverChapter.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Bookriver; + +public class BookRiverChapter +{ + [JsonPropertyName("id")] public long Id { get; set; } + + [JsonPropertyName("name")] public string Name { get; set; } +} diff --git a/src/Core/Types/Bookriver/BookRiverChapterContent.cs b/src/Core/Types/Bookriver/BookRiverChapterContent.cs new file mode 100644 index 00000000..350aae05 --- /dev/null +++ b/src/Core/Types/Bookriver/BookRiverChapterContent.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Bookriver; + +public class BookRiverChapterContent +{ + [JsonPropertyName("content")] public string Content { get; set; } +} diff --git a/src/Core/Types/Bookriver/BookRiverCurrentBook.cs b/src/Core/Types/Bookriver/BookRiverCurrentBook.cs new file mode 100644 index 00000000..5c5d27ba --- /dev/null +++ b/src/Core/Types/Bookriver/BookRiverCurrentBook.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Bookriver; + +public class BookRiverCurrentBook +{ + [JsonPropertyName("id")] public long Id { get; set; } +} diff --git a/src/Core/Types/Bookstab/BookstabApiResponse.cs b/src/Core/Types/Bookstab/BookstabApiResponse.cs new file mode 100644 index 00000000..d6c77728 --- /dev/null +++ b/src/Core/Types/Bookstab/BookstabApiResponse.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Bookstab; + +public class BookstabApiResponse +{ + [JsonPropertyName("book")] public BookstabBook Book { get; set; } + + [JsonPropertyName("chapter")] public BookstabChapter Chapter { get; set; } +} diff --git a/src/Core/Types/Bookstab/BookstabBook.cs b/src/Core/Types/Bookstab/BookstabBook.cs new file mode 100644 index 00000000..d01cc902 --- /dev/null +++ b/src/Core/Types/Bookstab/BookstabBook.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Bookstab; + +public class BookstabBook +{ + [JsonPropertyName("image")] public string Image { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("excerpt")] public string Excerpt { get; set; } + + [JsonPropertyName("user")] public BooksnabUser User { get; set; } + + [JsonPropertyName("chapters_show")] public BookstabChapter[] ChaptersShow { get; set; } +} diff --git a/src/Core/Types/Bookstab/BookstabChapter.cs b/src/Core/Types/Bookstab/BookstabChapter.cs new file mode 100644 index 00000000..3d455b5f --- /dev/null +++ b/src/Core/Types/Bookstab/BookstabChapter.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Bookstab; + +public class BookstabChapter +{ + [JsonPropertyName("id")] public int Id { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("body")] public string Body { get; set; } +} diff --git a/src/Core/Types/Bookstab/BookstabUser.cs b/src/Core/Types/Bookstab/BookstabUser.cs new file mode 100644 index 00000000..ef62bd8c --- /dev/null +++ b/src/Core/Types/Bookstab/BookstabUser.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Bookstab; + +public class BooksnabUser +{ + [JsonPropertyName("pseudonym")] public string Pseudonym { get; set; } + + [JsonPropertyName("name")] public string Name { get; set; } +} diff --git a/src/Core/Types/Bookstime/BookstimeAuthResponse.cs b/src/Core/Types/Bookstime/BookstimeAuthResponse.cs new file mode 100644 index 00000000..7b342e21 --- /dev/null +++ b/src/Core/Types/Bookstime/BookstimeAuthResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Bookstime; + +public class BookstimeAuthResponse +{ + [JsonPropertyName("X_OCTOBER_ERROR_MESSAGE")] public string XOctoberErrorMessage { get; set; } +} diff --git a/src/Core/Types/Boosty/BoostyApiResponse.cs b/src/Core/Types/Boosty/BoostyApiResponse.cs new file mode 100644 index 00000000..db5f6ba3 --- /dev/null +++ b/src/Core/Types/Boosty/BoostyApiResponse.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Boosty; + +public class BoostyApiResponse +{ + [JsonPropertyName("data")] public T Data { get; set; } + + [JsonPropertyName("extra")] public BoostyExtra Extra { get; set; } +} diff --git a/src/Core/Types/Boosty/BoostyExtra.cs b/src/Core/Types/Boosty/BoostyExtra.cs new file mode 100644 index 00000000..d33cee9c --- /dev/null +++ b/src/Core/Types/Boosty/BoostyExtra.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Boosty; + +public class BoostyExtra +{ + [JsonPropertyName("isLast")] public bool IsLast { get; set; } + + [JsonPropertyName("offset")] public string Offset { get; set; } +} diff --git a/src/Core/Types/Boosty/BoostyPost.cs b/src/Core/Types/Boosty/BoostyPost.cs new file mode 100644 index 00000000..a203fdc3 --- /dev/null +++ b/src/Core/Types/Boosty/BoostyPost.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Boosty; + +public class BoostyPost +{ + [JsonPropertyName("id")] public Guid Id { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("hasAccess")] public bool HasAccess { get; set; } +} diff --git a/src/Core/Types/Common/IdChapter.cs b/src/Core/Types/Common/IdChapter.cs new file mode 100644 index 00000000..82a703ac --- /dev/null +++ b/src/Core/Types/Common/IdChapter.cs @@ -0,0 +1,3 @@ +namespace Core.Types.Common; + +public record IdChapter(string Id, string Title); diff --git a/Core/Types/Common/TempFile.cs b/src/Core/Types/Common/TempFile.cs similarity index 69% rename from Core/Types/Common/TempFile.cs rename to src/Core/Types/Common/TempFile.cs index b094991e..07605673 100644 --- a/Core/Types/Common/TempFile.cs +++ b/src/Core/Types/Common/TempFile.cs @@ -1,52 +1,55 @@ -using System; -using System.IO; -using System.Threading.Tasks; +namespace Core.Types.Common; -namespace Core.Types.Common; - -public class TempFile : IDisposable { +public class TempFile : IDisposable +{ public string Name; public string Extension; - + public Uri Url { get; set; } - + public string Directory { get; set; } - public byte[] Content => File.Exists(FilePath) ? File.ReadAllBytes(FilePath) : []; + public byte[] Content => File.Exists(FilePath) ? File.ReadAllBytes(FilePath) : []; public string FullName => $"{Name}{Extension}"; - + public string FilePath => Path.Combine(Directory, FullName); public long Size => Content.Length; - - private TempFile(Uri url, string directory, string name, string extension) { + + private TempFile(Uri url, string directory, string name, string extension) + { Directory = directory; Url = url; Name = name; Extension = extension; } - - public static async Task Create(Uri url, string directory, string fullName, byte[] content) { + + public static async Task Create(Uri url, string directory, string fullName, byte[] content) + { return await Create(url, directory, Path.GetFileNameWithoutExtension(fullName), Path.GetExtension(fullName), content); } - - public static async Task Create(Uri url, string directory, string fullName, Stream stream) { + + public static async Task Create(Uri url, string directory, string fullName, Stream stream) + { return await Create(url, directory, Path.GetFileNameWithoutExtension(fullName), Path.GetExtension(fullName), stream); } - public static async Task Create(Uri url, string directory, string name, string extension, byte[] content) { + public static async Task Create(Uri url, string directory, string name, string extension, byte[] content) + { var image = new TempFile(url, directory, name, extension); await File.WriteAllBytesAsync(image.FilePath, content); return image; } - - public static async Task Create(Uri url, string directory, string name, string extension, Stream stream) { - if (stream.Length == 0) { - return default; + + public static async Task Create(Uri url, string directory, string name, string extension, Stream stream) + { + if (stream.Length == 0) + { + return null; } - + var tempFile = new TempFile(url, directory, name, extension); await using var file = File.OpenWrite(tempFile.FilePath); @@ -55,13 +58,16 @@ public static async Task Create(Uri url, string directory, string name return tempFile; } - public Stream GetStream() { + public Stream GetStream() + { return File.OpenRead(FilePath); } - public void Dispose() { - if (File.Exists(FilePath)) { + public void Dispose() + { + if (File.Exists(FilePath)) + { File.Delete(FilePath); } } -} \ No newline at end of file +} diff --git a/src/Core/Types/Common/UrlChapter.cs b/src/Core/Types/Common/UrlChapter.cs new file mode 100644 index 00000000..0a406d4e --- /dev/null +++ b/src/Core/Types/Common/UrlChapter.cs @@ -0,0 +1,3 @@ +namespace Core.Types.Common; + +public record UrlChapter(Uri Url, string Title); diff --git a/src/Core/Types/DarkNovels/DarkNovelsAuthData.cs b/src/Core/Types/DarkNovels/DarkNovelsAuthData.cs new file mode 100644 index 00000000..fb11e03a --- /dev/null +++ b/src/Core/Types/DarkNovels/DarkNovelsAuthData.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.DarkNovels; + +public class DarkNovelsAuthResponse +{ + [JsonPropertyName("token")] public DarkNovelsAuthToken Token { get; set; } +} diff --git a/src/Core/Types/DarkNovels/DarkNovelsAuthToken.cs b/src/Core/Types/DarkNovels/DarkNovelsAuthToken.cs new file mode 100644 index 00000000..f43ac4ca --- /dev/null +++ b/src/Core/Types/DarkNovels/DarkNovelsAuthToken.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.DarkNovels; + +public class DarkNovelsAuthToken +{ + [JsonPropertyName("accessToken")] public string AccessToken { get; set; } +} diff --git a/src/Core/Types/DarkNovels/DarkNovelsChapter.cs b/src/Core/Types/DarkNovels/DarkNovelsChapter.cs new file mode 100644 index 00000000..ebd9af05 --- /dev/null +++ b/src/Core/Types/DarkNovels/DarkNovelsChapter.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.DarkNovels; + +public class DarkNovelsChapter +{ + [JsonPropertyName("id")] public int Id { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("payed")] public int Payed { get; set; } +} diff --git a/src/Core/Types/DarkNovels/DarkNovelsData.cs b/src/Core/Types/DarkNovels/DarkNovelsData.cs new file mode 100644 index 00000000..dc2e59eb --- /dev/null +++ b/src/Core/Types/DarkNovels/DarkNovelsData.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.DarkNovels; + +public class DarkNovelsData +{ + [JsonPropertyName("status")] public string Status { get; set; } + + [JsonPropertyName("message")] public string Message { get; set; } + + [JsonPropertyName("data")] public T Data { get; set; } +} diff --git a/src/Core/Types/Dreame/DreameApiResponse.cs b/src/Core/Types/Dreame/DreameApiResponse.cs new file mode 100644 index 00000000..92a787b1 --- /dev/null +++ b/src/Core/Types/Dreame/DreameApiResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Dreame; + +public class DreameApiResponse +{ + [JsonPropertyName("data")] public T Data { get; set; } +} diff --git a/src/Core/Types/Dreame/DreameCatalog.cs b/src/Core/Types/Dreame/DreameCatalog.cs new file mode 100644 index 00000000..c0f73a84 --- /dev/null +++ b/src/Core/Types/Dreame/DreameCatalog.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Dreame; + +public class DreameCatalog +{ + [JsonPropertyName("pager")] public DreamePager Pager { get; set; } +} diff --git a/src/Core/Types/Dreame/DreameChapter.cs b/src/Core/Types/Dreame/DreameChapter.cs new file mode 100644 index 00000000..06b06dd3 --- /dev/null +++ b/src/Core/Types/Dreame/DreameChapter.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Dreame; + +public class DreameChapter +{ + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("id")] public string Id { get; set; } + + [JsonPropertyName("content")] public string Content { get; set; } +} diff --git a/src/Core/Types/Dreame/DreameNovel.cs b/src/Core/Types/Dreame/DreameNovel.cs new file mode 100644 index 00000000..3062b18e --- /dev/null +++ b/src/Core/Types/Dreame/DreameNovel.cs @@ -0,0 +1,20 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Dreame; + +public class DreameNovel +{ + [JsonPropertyName("id")] public long Id { get; set; } + + [JsonPropertyName("author_id")] public string AuthorId { get; set; } + + [JsonPropertyName("author_name")] public string AuthorName { get; set; } + + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("descr")] public string Description { get; set; } + + [JsonPropertyName("language")] public string Language { get; set; } + + [JsonPropertyName("cover_url")] public string Cover { get; set; } +} diff --git a/src/Core/Types/Dreame/DreamePager.cs b/src/Core/Types/Dreame/DreamePager.cs new file mode 100644 index 00000000..021a1b2d --- /dev/null +++ b/src/Core/Types/Dreame/DreamePager.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Dreame; + +public class DreamePager +{ + [JsonPropertyName("chap_list")] public List ChapterList { get; set; } +} diff --git a/src/Core/Types/FanFicus/FanFicusApiResponse.cs b/src/Core/Types/FanFicus/FanFicusApiResponse.cs new file mode 100644 index 00000000..e668d16c --- /dev/null +++ b/src/Core/Types/FanFicus/FanFicusApiResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.FanFicus; + +public class FanFicusApiResponse +{ + [JsonPropertyName("value")] public T Value { get; set; } +} diff --git a/src/Core/Types/FanFicus/FanFicusBook.cs b/src/Core/Types/FanFicus/FanFicusBook.cs new file mode 100644 index 00000000..d2da3f03 --- /dev/null +++ b/src/Core/Types/FanFicus/FanFicusBook.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.FanFicus; + +public class FanFicusBook +{ + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("description")] public string Description { get; set; } + + [JsonPropertyName("creatorId")] public FanFicusCreator[] Creators { get; set; } + + [JsonPropertyName("images")] public FanFicusImage[] Images { get; set; } +} diff --git a/src/Core/Types/FanFicus/FanFicusChapter.cs b/src/Core/Types/FanFicus/FanFicusChapter.cs new file mode 100644 index 00000000..ca2c2f78 --- /dev/null +++ b/src/Core/Types/FanFicus/FanFicusChapter.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.FanFicus; + +public class FanFicusChapter +{ + [JsonPropertyName("text")] public string Text { get; set; } +} diff --git a/src/Core/Types/FanFicus/FanFicusCreator.cs b/src/Core/Types/FanFicus/FanFicusCreator.cs new file mode 100644 index 00000000..8ea2962c --- /dev/null +++ b/src/Core/Types/FanFicus/FanFicusCreator.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.FanFicus; + +public class FanFicusCreator +{ + [JsonPropertyName("_id")] public string Id { get; set; } + + [JsonPropertyName("nickName")] public string NickName { get; set; } +} diff --git a/src/Core/Types/FanFicus/FanFicusImage.cs b/src/Core/Types/FanFicus/FanFicusImage.cs new file mode 100644 index 00000000..b42d04e2 --- /dev/null +++ b/src/Core/Types/FanFicus/FanFicusImage.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.FanFicus; + +public class FanFicusImage +{ + [JsonPropertyName("url")] public string Url { get; set; } +} diff --git a/src/Core/Types/FanFicus/FanFicusPart.cs b/src/Core/Types/FanFicus/FanFicusPart.cs new file mode 100644 index 00000000..e553d17f --- /dev/null +++ b/src/Core/Types/FanFicus/FanFicusPart.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.FanFicus; + +public class FanFicusPart +{ + [JsonPropertyName("_id")] public string Id { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } +} diff --git a/src/Core/Types/FanFicus/FanFicusUser.cs b/src/Core/Types/FanFicus/FanFicusUser.cs new file mode 100644 index 00000000..8eff5e7e --- /dev/null +++ b/src/Core/Types/FanFicus/FanFicusUser.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.FanFicus; + +public class FanFicusUser +{ + [JsonPropertyName("token")] public string Token { get; set; } +} diff --git a/src/Core/Types/Freedlit/FreedlitApiResponse.cs b/src/Core/Types/Freedlit/FreedlitApiResponse.cs new file mode 100644 index 00000000..4c849db6 --- /dev/null +++ b/src/Core/Types/Freedlit/FreedlitApiResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Freedlit; + +public class FreedlitApiResponse +{ + [JsonPropertyName("success")] public T Success { get; set; } +} diff --git a/src/Core/Types/Freedlit/FreedlitApp.cs b/src/Core/Types/Freedlit/FreedlitApp.cs new file mode 100644 index 00000000..4ad1add7 --- /dev/null +++ b/src/Core/Types/Freedlit/FreedlitApp.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Freedlit; + +public class FreedlitApp +{ + [JsonPropertyName("props")] public FreedlitProps Props { get; set; } +} diff --git a/src/Core/Types/Freedlit/FreedlitAuthError.cs b/src/Core/Types/Freedlit/FreedlitAuthError.cs new file mode 100644 index 00000000..62d91adc --- /dev/null +++ b/src/Core/Types/Freedlit/FreedlitAuthError.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Freedlit; + +public class FreedlitAuthError +{ + [JsonPropertyName("email")] public string Email { get; set; } + + [JsonPropertyName("password")] public string[] Password { get; set; } +} diff --git a/src/Core/Types/Freedlit/FreedlitAuthor.cs b/src/Core/Types/Freedlit/FreedlitAuthor.cs new file mode 100644 index 00000000..a5316187 --- /dev/null +++ b/src/Core/Types/Freedlit/FreedlitAuthor.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Freedlit; + +public class FreedlitAuthor +{ + [JsonPropertyName("id")] public long Id { get; set; } + + [JsonPropertyName("user_link")] public string UserLink { get; set; } + + [JsonPropertyName("name")] public string Name { get; set; } +} diff --git a/src/Core/Types/Freedlit/FreedlitBook.cs b/src/Core/Types/Freedlit/FreedlitBook.cs new file mode 100644 index 00000000..bef06ad7 --- /dev/null +++ b/src/Core/Types/Freedlit/FreedlitBook.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Freedlit; + +public class FreedlitBook +{ + [JsonPropertyName("id")] public long Id { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("cover")] public string Cover { get; set; } + + [JsonPropertyName("annotation")] public string Annotation { get; set; } + + [JsonPropertyName("language")] public string Language { get; set; } + + [JsonPropertyName("main_author")] public FreedlitAuthor MainAuthor { get; set; } +} diff --git a/src/Core/Types/Freedlit/FreedlitChapter.cs b/src/Core/Types/Freedlit/FreedlitChapter.cs new file mode 100644 index 00000000..63c1de48 --- /dev/null +++ b/src/Core/Types/Freedlit/FreedlitChapter.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Freedlit; + +public class FreedlitChapter +{ + [JsonPropertyName("id")] public long Id { get; set; } + + [JsonPropertyName("header")] public string Header { get; set; } + + [JsonPropertyName("content")] public string Content { get; set; } +} diff --git a/src/Core/Types/Freedlit/FreedlitItemsContent.cs b/src/Core/Types/Freedlit/FreedlitItemsContent.cs new file mode 100644 index 00000000..dc2151b3 --- /dev/null +++ b/src/Core/Types/Freedlit/FreedlitItemsContent.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Freedlit; + +public class FreedlitItemsContent +{ + [JsonPropertyName("items")] public List Items { get; set; } +} diff --git a/src/Core/Types/Freedlit/FreedlitProps.cs b/src/Core/Types/Freedlit/FreedlitProps.cs new file mode 100644 index 00000000..d9a84d5e --- /dev/null +++ b/src/Core/Types/Freedlit/FreedlitProps.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Freedlit; + +public class FreedlitProps +{ + [JsonPropertyName("book")] public FreedlitBook Book { get; set; } + + [JsonPropertyName("errors")] public TErr Errors { get; set; } +} diff --git a/src/Core/Types/Freedlit/FreedlitSuccessResponse.cs b/src/Core/Types/Freedlit/FreedlitSuccessResponse.cs new file mode 100644 index 00000000..d7069700 --- /dev/null +++ b/src/Core/Types/Freedlit/FreedlitSuccessResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Freedlit; + +public class FreedlitSuccessResponse +{ + [JsonPropertyName("items")] public List Items { get; set; } +} diff --git a/src/Core/Types/HotNovelPub/HotNovelPubApiResponse.cs b/src/Core/Types/HotNovelPub/HotNovelPubApiResponse.cs new file mode 100644 index 00000000..1c14bb4e --- /dev/null +++ b/src/Core/Types/HotNovelPub/HotNovelPubApiResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.HotNovelPub; + +public class HotNovelPubApiResponse +{ + [JsonPropertyName("data")] public T Data { get; set; } +} diff --git a/src/Core/Types/HotNovelPub/HotNovelPubAuthorize.cs b/src/Core/Types/HotNovelPub/HotNovelPubAuthorize.cs new file mode 100644 index 00000000..19df3a4b --- /dev/null +++ b/src/Core/Types/HotNovelPub/HotNovelPubAuthorize.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.HotNovelPub; + +public class HotNovelPubAuthorize +{ + [JsonPropertyName("id")] public int Id { get; set; } + + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("slug")] public string Slug { get; set; } +} diff --git a/src/Core/Types/HotNovelPub/HotNovelPubBook.cs b/src/Core/Types/HotNovelPub/HotNovelPubBook.cs new file mode 100644 index 00000000..f26d2765 --- /dev/null +++ b/src/Core/Types/HotNovelPub/HotNovelPubBook.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.HotNovelPub; + +public class HotNovelPubBook +{ + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("description")] public string Description { get; set; } + + [JsonPropertyName("authorize")] public HotNovelPubAuthorize Authorize { get; set; } + + [JsonPropertyName("image")] public string Image { get; set; } +} diff --git a/src/Core/Types/HotNovelPub/HotNovelPubBookResponse.cs b/src/Core/Types/HotNovelPub/HotNovelPubBookResponse.cs new file mode 100644 index 00000000..211e3e25 --- /dev/null +++ b/src/Core/Types/HotNovelPub/HotNovelPubBookResponse.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.HotNovelPub; + +public class HotNovelPubBookResponse +{ + [JsonPropertyName("book")] public HotNovelPubBook Book { get; set; } + + [JsonPropertyName("chapters")] public List Chapters { get; set; } +} diff --git a/src/Core/Types/HotNovelPub/HotNovelPubChapter.cs b/src/Core/Types/HotNovelPub/HotNovelPubChapter.cs new file mode 100644 index 00000000..211e4bc9 --- /dev/null +++ b/src/Core/Types/HotNovelPub/HotNovelPubChapter.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.HotNovelPub; + +public class HotNovelPubChapter +{ + [JsonPropertyName("id")] public int Id { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("slug")] public string Slug { get; set; } +} diff --git a/src/Core/Types/Librebook/LibrebookAuthResponse.cs b/src/Core/Types/Librebook/LibrebookAuthResponse.cs new file mode 100644 index 00000000..f4984ede --- /dev/null +++ b/src/Core/Types/Librebook/LibrebookAuthResponse.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Librebook; + +public class LibrebookAuthResponse +{ + [JsonPropertyName("type")] public string Type { get; set; } + + [JsonPropertyName("text")] public string Text { get; set; } +} diff --git a/src/Core/Types/Litexit/LitexitChapter.cs b/src/Core/Types/Litexit/LitexitChapter.cs new file mode 100644 index 00000000..9d38d3b3 --- /dev/null +++ b/src/Core/Types/Litexit/LitexitChapter.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litexit; + +public class LitexitChapter +{ + [JsonPropertyName("id")] public int Id { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("text")] public string Text { get; set; } +} diff --git a/src/Core/Types/Litexit/LitexitUser.cs b/src/Core/Types/Litexit/LitexitUser.cs new file mode 100644 index 00000000..b4a4706f --- /dev/null +++ b/src/Core/Types/Litexit/LitexitUser.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litexit; + +public class LitexitUser +{ + [JsonPropertyName("id")] public int Id { get; set; } +} diff --git a/src/Core/Types/Litgorod/LitgorodAuthResponse.cs b/src/Core/Types/Litgorod/LitgorodAuthResponse.cs new file mode 100644 index 00000000..9be9dabc --- /dev/null +++ b/src/Core/Types/Litgorod/LitgorodAuthResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litgorod; + +public class LitgorodAuthResponse +{ + [JsonPropertyName("message")] public string Message { get; set; } +} diff --git a/src/Core/Types/Litgorod/LitgorodBookPart.cs b/src/Core/Types/Litgorod/LitgorodBookPart.cs new file mode 100644 index 00000000..873c82a1 --- /dev/null +++ b/src/Core/Types/Litgorod/LitgorodBookPart.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litgorod; + +public class LitgorodBookPart +{ + [JsonPropertyName("text")] public string Text { get; set; } +} diff --git a/src/Core/Types/Litgorod/LitgorodChapter.cs b/src/Core/Types/Litgorod/LitgorodChapter.cs new file mode 100644 index 00000000..7cbca461 --- /dev/null +++ b/src/Core/Types/Litgorod/LitgorodChapter.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litgorod; + +public class LitgorodChapter +{ + [JsonPropertyName("explodedParagraphs")] public string[] Paragraphs { get; set; } +} diff --git a/src/Core/Types/Litmarket/AuthResponse.cs b/src/Core/Types/Litmarket/AuthResponse.cs new file mode 100644 index 00000000..ad28974b --- /dev/null +++ b/src/Core/Types/Litmarket/AuthResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litmarket; + +public class AuthResponse +{ + [JsonPropertyName("success")] public bool Success { get; set; } +} diff --git a/src/Core/Types/Litmarket/Block.cs b/src/Core/Types/Litmarket/Block.cs new file mode 100644 index 00000000..0d07077e --- /dev/null +++ b/src/Core/Types/Litmarket/Block.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litmarket; + +public class Block +{ + [JsonPropertyName("chunk")] public Chunk Chunk { get; set; } + + [JsonPropertyName("index")] public int Index { get; set; } +} diff --git a/src/Core/Types/Litmarket/Chunk.cs b/src/Core/Types/Litmarket/Chunk.cs new file mode 100644 index 00000000..0b825cbd --- /dev/null +++ b/src/Core/Types/Litmarket/Chunk.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litmarket; + +public class Chunk +{ + [JsonPropertyName("mods")] public Mod[] Mods { get; set; } +} diff --git a/src/Core/Types/Litmarket/LBook.cs b/src/Core/Types/Litmarket/LBook.cs new file mode 100644 index 00000000..689f5f57 --- /dev/null +++ b/src/Core/Types/Litmarket/LBook.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litmarket; + +public class LBook +{ + [JsonPropertyName("ebookId")] public int EbookId { get; set; } +} diff --git a/src/Core/Types/Litmarket/Mod.cs b/src/Core/Types/Litmarket/Mod.cs new file mode 100644 index 00000000..29572b1b --- /dev/null +++ b/src/Core/Types/Litmarket/Mod.cs @@ -0,0 +1,17 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Core.Types.Litmarket; + +public class Mod +{ + [JsonPropertyName("type")] public string Type { get; set; } + + [JsonPropertyName("styles")] public string[] Styles { get; set; } + + [JsonPropertyName("text")] public string Text { get; set; } + + [JsonPropertyName("data")] public JsonElement Data { get; set; } + + [JsonPropertyName("mods")] public Mod[] Mods { get; set; } +} diff --git a/src/Core/Types/Litmarket/Response.cs b/src/Core/Types/Litmarket/Response.cs new file mode 100644 index 00000000..fe518b08 --- /dev/null +++ b/src/Core/Types/Litmarket/Response.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litmarket; + +public class Response +{ + [JsonPropertyName("book")] public LBook Book { get; set; } + + [JsonPropertyName("tableOfContent")] public string Toc { get; set; } +} diff --git a/src/Core/Types/Litnet/LitnetAuthResponse.cs b/src/Core/Types/Litnet/LitnetAuthResponse.cs new file mode 100644 index 00000000..fb66d4b8 --- /dev/null +++ b/src/Core/Types/Litnet/LitnetAuthResponse.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litnet; + +public class LitnetAuthResponse +{ + [JsonPropertyName("error")] public string Error { get; set; } + + [JsonPropertyName("token")] public string Token { get; set; } +} diff --git a/src/Core/Types/Litnet/LitnetBookResponse.cs b/src/Core/Types/Litnet/LitnetBookResponse.cs new file mode 100644 index 00000000..b341a2f5 --- /dev/null +++ b/src/Core/Types/Litnet/LitnetBookResponse.cs @@ -0,0 +1,28 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litnet; + +public class LitnetBookResponse +{ + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("author_name")] public string AuthorName { get; set; } + + [JsonPropertyName("cover")] public string Cover { get; set; } + + [JsonPropertyName("annotation")] public string Annotation { get; set; } + + [JsonPropertyName("url")] public string Url { get; set; } + + [JsonPropertyName("author_id")] public long AuthorId { get; set; } + + [JsonPropertyName("lang")] public string Lang { get; set; } + + [JsonPropertyName("cycle_priority")] public int? CyclePriority { get; set; } + + [JsonPropertyName("adult_only")] public bool AdultOnly { get; set; } + + [JsonPropertyName("co_author_name")] public string CoAuthorName { get; set; } + + [JsonPropertyName("co_author")] public long? CoAuthorId { get; set; } +} diff --git a/src/Core/Types/Litnet/LitnetChapterResponse.cs b/src/Core/Types/Litnet/LitnetChapterResponse.cs new file mode 100644 index 00000000..d88052db --- /dev/null +++ b/src/Core/Types/Litnet/LitnetChapterResponse.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litnet; + +public class LitnetChapterResponse +{ + [JsonPropertyName("id")] public long Id { get; set; } + + [JsonPropertyName("text")] public string Text { get; set; } +} diff --git a/src/Core/Types/Litnet/LitnetContentsResponse.cs b/src/Core/Types/Litnet/LitnetContentsResponse.cs new file mode 100644 index 00000000..d7e381f8 --- /dev/null +++ b/src/Core/Types/Litnet/LitnetContentsResponse.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litnet; + +public class LitnetContentsResponse +{ + [JsonPropertyName("id")] public long Id { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } +} diff --git a/Core/Types/Litres/LitresBook.cs b/src/Core/Types/Litres/LitresBook.cs similarity index 56% rename from Core/Types/Litres/LitresBook.cs rename to src/Core/Types/Litres/LitresBook.cs index d3261202..361a9f44 100644 --- a/Core/Types/Litres/LitresBook.cs +++ b/src/Core/Types/Litres/LitresBook.cs @@ -1,10 +1,10 @@ -using System.Collections.Generic; using HtmlAgilityPack; -namespace Core.Types.Litres; +namespace Core.Types.Litres; -public class LitresBook { +public class LitresBook +{ public HtmlDocument Content; public Dictionary Targets = new(); -} \ No newline at end of file +} diff --git a/src/Core/Types/Litres/LitresPayload.cs b/src/Core/Types/Litres/LitresPayload.cs new file mode 100644 index 00000000..e2003aed --- /dev/null +++ b/src/Core/Types/Litres/LitresPayload.cs @@ -0,0 +1,40 @@ +using System.Security.Cryptography; +using System.Text; +using System.Text.Json.Serialization; + +namespace Core.Types.Litres; + +public class LitresPayload +{ + [JsonPropertyName("sha")] public string Sha { get; set; } + + [JsonPropertyName("sid")] public string Sid { get; set; } + + [JsonPropertyName("uilang")] public string UiLang { get; set; } = "rus"; + + [JsonPropertyName("time")] public string Time { get; set; } + + [JsonPropertyName("mobile_app")] public string App { get; set; } + + [JsonPropertyName("requests")] public List Requests { get; set; } = new(); + + public static LitresPayload Create(DateTime date, string sid, string secret, string app) + { + var result = new LitresPayload + { + Time = date.ToString("yyyy-MM-ddTHH:mm:sszzzz"), App = app, Sid = sid + }; + + result.Sha = Sha256(result.Time + secret); + + return result; + } + + private static string Sha256(string str) + { + var crypt = SHA256.Create(); + var crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(str), 0, Encoding.UTF8.GetByteCount(str)); + + return crypto.Aggregate(new StringBuilder(), (sb, b) => sb.Append(b.ToString("x2"))).ToString(); + } +} diff --git a/Core/Types/Litres/LitresTarget.cs b/src/Core/Types/Litres/LitresTarget.cs similarity index 66% rename from Core/Types/Litres/LitresTarget.cs rename to src/Core/Types/Litres/LitresTarget.cs index b33ee445..1bae77ad 100644 --- a/Core/Types/Litres/LitresTarget.cs +++ b/src/Core/Types/Litres/LitresTarget.cs @@ -1,7 +1,8 @@ -namespace Core.Types.Litres; +namespace Core.Types.Litres; -public class LitresTarget { +public class LitresTarget +{ public string Id { get; set; } public string Target { get; set; } public byte[] Content { get; set; } -} \ No newline at end of file +} diff --git a/src/Core/Types/Litres/Requests/LitresAuthData.cs b/src/Core/Types/Litres/Requests/LitresAuthData.cs new file mode 100644 index 00000000..e19fb8eb --- /dev/null +++ b/src/Core/Types/Litres/Requests/LitresAuthData.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litres.Requests; + +public class LitresAuthData +{ + [JsonPropertyName("login")] public string Login { get; set; } + + [JsonPropertyName("pwd")] public string Password { get; set; } +} diff --git a/Core/Types/Litres/Requests/LitresAuthRequest.cs b/src/Core/Types/Litres/Requests/LitresAuthRequest.cs similarity index 59% rename from Core/Types/Litres/Requests/LitresAuthRequest.cs rename to src/Core/Types/Litres/Requests/LitresAuthRequest.cs index 5c71d4a4..2011fe80 100644 --- a/Core/Types/Litres/Requests/LitresAuthRequest.cs +++ b/src/Core/Types/Litres/Requests/LitresAuthRequest.cs @@ -1,9 +1,11 @@ -namespace Core.Types.Litres.Requests; +namespace Core.Types.Litres.Requests; -public class LitresAuthRequest : LitresRequestBase { - public LitresAuthRequest(string login, string password) { +public class LitresAuthRequest : LitresRequestBase +{ + public LitresAuthRequest(string login, string password) + { Func = "w_create_sid"; Param.Login = login; Param.Password = password; } -} \ No newline at end of file +} diff --git a/src/Core/Types/Litres/Requests/LitresBrowseArtsData.cs b/src/Core/Types/Litres/Requests/LitresBrowseArtsData.cs new file mode 100644 index 00000000..e4540326 --- /dev/null +++ b/src/Core/Types/Litres/Requests/LitresBrowseArtsData.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litres.Requests; + +public class LitresBrowseArtsData +{ + [JsonPropertyName("currency")] public string Currency { get; set; } + + [JsonPropertyName("anno")] public string Anno { get; set; } + + [JsonPropertyName("id")] public string[] Id { get; set; } +} diff --git a/Core/Types/Litres/Requests/LitresBrowseArtsRequest.cs b/src/Core/Types/Litres/Requests/LitresBrowseArtsRequest.cs similarity index 62% rename from Core/Types/Litres/Requests/LitresBrowseArtsRequest.cs rename to src/Core/Types/Litres/Requests/LitresBrowseArtsRequest.cs index ac812e17..2827d6ff 100644 --- a/Core/Types/Litres/Requests/LitresBrowseArtsRequest.cs +++ b/src/Core/Types/Litres/Requests/LitresBrowseArtsRequest.cs @@ -1,10 +1,12 @@ -namespace Core.Types.Litres.Requests; +namespace Core.Types.Litres.Requests; -public class LitresBrowseArtsRequest : LitresRequestBase { - public LitresBrowseArtsRequest(string[] id) { +public class LitresBrowseArtsRequest : LitresRequestBase +{ + public LitresBrowseArtsRequest(string[] id) + { Func = "r_browse_arts"; Param.Currency = "RUB"; Param.Anno = "1"; Param.Id = id; } -} \ No newline at end of file +} diff --git a/src/Core/Types/Litres/Requests/LitresRequestBase.cs b/src/Core/Types/Litres/Requests/LitresRequestBase.cs new file mode 100644 index 00000000..81ec054c --- /dev/null +++ b/src/Core/Types/Litres/Requests/LitresRequestBase.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litres.Requests; + +public abstract class LitresRequestBase where T : new() +{ + [JsonPropertyName("id")] public string Id { get; set; } = "data"; + + [JsonPropertyName("func")] public string Func { get; set; } + + [JsonPropertyName("param")] public T Param { get; } = new(); +} diff --git a/src/Core/Types/Litres/Response/LitresArt.cs b/src/Core/Types/Litres/Response/LitresArt.cs new file mode 100644 index 00000000..d9c51e47 --- /dev/null +++ b/src/Core/Types/Litres/Response/LitresArt.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litres.Response; + +public class LitresArt +{ + [JsonPropertyName("id")] public long Id { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("html_annotation")] public string Annotation { get; set; } + + [JsonPropertyName("persons")] public LitresPerson[] Persons { get; set; } = []; + + [JsonPropertyName("cover_url")] public string Cover { get; set; } + + [JsonPropertyName("series")] public LitresSeria[] Sequences { get; set; } = []; + + [JsonPropertyName("linked_arts")] public LitresArt[] LinkedArts { get; set; } + + [JsonPropertyName("art_type")] public LitresArtTypeEnum ArtType { get; set; } +} diff --git a/Core/Types/Litres/Response/LitresArtTypeEnum.cs b/src/Core/Types/Litres/Response/LitresArtTypeEnum.cs similarity index 70% rename from Core/Types/Litres/Response/LitresArtTypeEnum.cs rename to src/Core/Types/Litres/Response/LitresArtTypeEnum.cs index 8605135a..ea5ddd0b 100644 --- a/Core/Types/Litres/Response/LitresArtTypeEnum.cs +++ b/src/Core/Types/Litres/Response/LitresArtTypeEnum.cs @@ -1,7 +1,8 @@ namespace Core.Types.Litres.Response; -public enum LitresArtTypeEnum { +public enum LitresArtTypeEnum +{ Text = 0, Audio = 1, Pdf = 4 -} \ No newline at end of file +} diff --git a/src/Core/Types/Litres/Response/LitresAuthResponseData.cs b/src/Core/Types/Litres/Response/LitresAuthResponseData.cs new file mode 100644 index 00000000..77c9ef5f --- /dev/null +++ b/src/Core/Types/Litres/Response/LitresAuthResponseData.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litres.Response; + +public class LitresAuthResponseData +{ + [JsonPropertyName("success")] public bool Success { get; set; } + + [JsonPropertyName("sid")] public string Sid { get; set; } + + [JsonPropertyName("error_message")] public string ErrorMessage { get; set; } +} diff --git a/src/Core/Types/Litres/Response/LitresFile.cs b/src/Core/Types/Litres/Response/LitresFile.cs new file mode 100644 index 00000000..7f0c54c5 --- /dev/null +++ b/src/Core/Types/Litres/Response/LitresFile.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litres.Response; + +public class LitresFile +{ + [JsonPropertyName("extension")] public string Extension { get; set; } + + [JsonPropertyName("id")] public long Id { get; set; } +} diff --git a/src/Core/Types/Litres/Response/LitresFiles.cs b/src/Core/Types/Litres/Response/LitresFiles.cs new file mode 100644 index 00000000..97b99f60 --- /dev/null +++ b/src/Core/Types/Litres/Response/LitresFiles.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litres.Response; + +public class LitresFiles +{ + [JsonPropertyName("files")] public LitresFile[] Files { get; set; } +} diff --git a/src/Core/Types/Litres/Response/LitresMe.cs b/src/Core/Types/Litres/Response/LitresMe.cs new file mode 100644 index 00000000..cfce9205 --- /dev/null +++ b/src/Core/Types/Litres/Response/LitresMe.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litres.Response; + +public class LitresMe +{ + [JsonPropertyName("partner_subscriptions")] public LitresPartnerSubscriptions PartnerSubscriptions { get; set; } +} diff --git a/src/Core/Types/Litres/Response/LitresPartnerSubscriptions.cs b/src/Core/Types/Litres/Response/LitresPartnerSubscriptions.cs new file mode 100644 index 00000000..1d665297 --- /dev/null +++ b/src/Core/Types/Litres/Response/LitresPartnerSubscriptions.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litres.Response; + +public class LitresPartnerSubscriptions +{ + [JsonPropertyName("subscriptions")] public LitresSubscription[] Subscriptions { get; set; } +} diff --git a/src/Core/Types/Litres/Response/LitresPerson.cs b/src/Core/Types/Litres/Response/LitresPerson.cs new file mode 100644 index 00000000..48aa9fdd --- /dev/null +++ b/src/Core/Types/Litres/Response/LitresPerson.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litres.Response; + +public class LitresPerson +{ + [JsonPropertyName("id")] public TId Id { get; set; } + + [JsonPropertyName("full_name")] public string FullName { get; set; } + + [JsonPropertyName("role")] public string Role { get; set; } + + [JsonPropertyName("url")] public string Url { get; set; } +} diff --git a/src/Core/Types/Litres/Response/LitresResponse.cs b/src/Core/Types/Litres/Response/LitresResponse.cs new file mode 100644 index 00000000..e837d3ad --- /dev/null +++ b/src/Core/Types/Litres/Response/LitresResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litres.Response; + +public class LitresResponse +{ + [JsonPropertyName("data")] public T Data { get; set; } +} diff --git a/src/Core/Types/Litres/Response/LitresSeria.cs b/src/Core/Types/Litres/Response/LitresSeria.cs new file mode 100644 index 00000000..81e1a82c --- /dev/null +++ b/src/Core/Types/Litres/Response/LitresSeria.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litres.Response; + +public class LitresSeria +{ + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("id")] public long Id { get; set; } + + [JsonPropertyName("sequence_number")] public string SequenceNumber { get; set; } +} diff --git a/src/Core/Types/Litres/Response/LitresStaticResponse.cs b/src/Core/Types/Litres/Response/LitresStaticResponse.cs new file mode 100644 index 00000000..bebc55b9 --- /dev/null +++ b/src/Core/Types/Litres/Response/LitresStaticResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litres.Response; + +public class LitresStaticResponse +{ + [JsonPropertyName("payload")] public LitresResponse Payload { get; set; } +} diff --git a/src/Core/Types/Litres/Response/LitresSubscription.cs b/src/Core/Types/Litres/Response/LitresSubscription.cs new file mode 100644 index 00000000..c1a4894a --- /dev/null +++ b/src/Core/Types/Litres/Response/LitresSubscription.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litres.Response; + +public class LitresSubscription +{ + [JsonPropertyName("is_active")] public bool IsActive { get; set; } + + [JsonPropertyName("host")] public string Host { get; set; } + + [JsonPropertyName("type")] public string Type { get; set; } +} diff --git a/src/Core/Types/Litsovet/LitsovetLoginResponse.cs b/src/Core/Types/Litsovet/LitsovetLoginResponse.cs new file mode 100644 index 00000000..9ce32185 --- /dev/null +++ b/src/Core/Types/Litsovet/LitsovetLoginResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Litsovet; + +public class LitsovetLoginResponse +{ + [JsonPropertyName("ok")] public int Ok { get; set; } +} diff --git a/src/Core/Types/MangaLib/MangaLibPg.cs b/src/Core/Types/MangaLib/MangaLibPg.cs new file mode 100644 index 00000000..7bf9f03c --- /dev/null +++ b/src/Core/Types/MangaLib/MangaLibPg.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.MangaLib; + +public class MangaLibPg +{ + [JsonPropertyName("p")] public int P { get; set; } + + [JsonPropertyName("u")] public string U { get; set; } +} diff --git a/src/Core/Types/MyBook/MyBookAuth.cs b/src/Core/Types/MyBook/MyBookAuth.cs new file mode 100644 index 00000000..6c193e58 --- /dev/null +++ b/src/Core/Types/MyBook/MyBookAuth.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.MyBook; + +public class MyBookAuth +{ + [JsonPropertyName("token")] public string Token { get; set; } + + [JsonPropertyName("secret")] public string Secret { get; set; } +} diff --git a/src/Core/Types/MyBook/MyBookAuthor.cs b/src/Core/Types/MyBook/MyBookAuthor.cs new file mode 100644 index 00000000..8fcae065 --- /dev/null +++ b/src/Core/Types/MyBook/MyBookAuthor.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.MyBook; + +public class MyBookAuthor +{ + [JsonPropertyName("cover_name")] public string Name { get; set; } + + [JsonPropertyName("absolute_url")] public string Url { get; set; } +} diff --git a/src/Core/Types/MyBook/MyBookBook.cs b/src/Core/Types/MyBook/MyBookBook.cs new file mode 100644 index 00000000..edd5eb80 --- /dev/null +++ b/src/Core/Types/MyBook/MyBookBook.cs @@ -0,0 +1,27 @@ +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace Core.Types.MyBook; + +public class MyBookBook +{ + [JsonPropertyName("id")] public long Id { get; set; } + + [JsonPropertyName("annotation")] public string Annotation { get; set; } + + [JsonPropertyName("bookfile")] public string BookFile { get; set; } + + [JsonPropertyName("default_cover")] public string Cover { get; set; } + + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("authors")] public MyBookAuthor[] Authors { get; set; } + + [JsonPropertyName("type")] public string Type { get; set; } + + [JsonPropertyName("mapfiles")] public MyBookMapFile[] MapFiles { get; set; } + + [JsonPropertyName("connected_book")] public MyBookBook Connected { get; set; } + + [JsonPropertyName("bookfiles")] public JsonNode[] Files { get; set; } +} diff --git a/src/Core/Types/MyBook/MyBookFile.cs b/src/Core/Types/MyBook/MyBookFile.cs new file mode 100644 index 00000000..ddc72f88 --- /dev/null +++ b/src/Core/Types/MyBook/MyBookFile.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.MyBook; + +public class MyBookFile +{ + [JsonPropertyName("url")] public string Url { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("order")] public int Order { get; set; } +} diff --git a/src/Core/Types/MyBook/MyBookMapFile.cs b/src/Core/Types/MyBook/MyBookMapFile.cs new file mode 100644 index 00000000..2c4c6656 --- /dev/null +++ b/src/Core/Types/MyBook/MyBookMapFile.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.MyBook; + +public class MyBookMapFile +{ + [JsonPropertyName("book")] public long Book { get; set; } +} diff --git a/src/Core/Types/Neobook/NeobookAttachment.cs b/src/Core/Types/Neobook/NeobookAttachment.cs new file mode 100644 index 00000000..1f43fb55 --- /dev/null +++ b/src/Core/Types/Neobook/NeobookAttachment.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Neobook; + +public class NeobookAttachment +{ + [JsonPropertyName("image")] public Dictionary Cover { get; set; } +} diff --git a/src/Core/Types/Neobook/NeobookAuth.cs b/src/Core/Types/Neobook/NeobookAuth.cs new file mode 100644 index 00000000..aa425870 --- /dev/null +++ b/src/Core/Types/Neobook/NeobookAuth.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Neobook; + +public class NeobookAuth +{ + [JsonPropertyName("error")] public string Error { get; set; } + + [JsonPropertyName("login")] public NeobookLogin Login { get; set; } +} diff --git a/src/Core/Types/Neobook/NeobookBook.cs b/src/Core/Types/Neobook/NeobookBook.cs new file mode 100644 index 00000000..3ea58aad --- /dev/null +++ b/src/Core/Types/Neobook/NeobookBook.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Neobook; + +public class NeobookBook +{ + [JsonPropertyName("chapters")] public NeobookChapter[] Chapters { get; set; } + + [JsonPropertyName("active_chapter_index")] public int ActiveChapterIndex { get; set; } +} diff --git a/src/Core/Types/Neobook/NeobookChapter.cs b/src/Core/Types/Neobook/NeobookChapter.cs new file mode 100644 index 00000000..9e391174 --- /dev/null +++ b/src/Core/Types/Neobook/NeobookChapter.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Neobook; + +public class NeobookChapter +{ + [JsonPropertyName("data")] public NeobookChapterData Data { get; set; } +} diff --git a/src/Core/Types/Neobook/NeobookChapterData.cs b/src/Core/Types/Neobook/NeobookChapterData.cs new file mode 100644 index 00000000..85e39cdf --- /dev/null +++ b/src/Core/Types/Neobook/NeobookChapterData.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Neobook; + +public class NeobookChapterData +{ + [JsonPropertyName("html")] public string Html { get; set; } +} diff --git a/src/Core/Types/Neobook/NeobookLogin.cs b/src/Core/Types/Neobook/NeobookLogin.cs new file mode 100644 index 00000000..1d2eaffb --- /dev/null +++ b/src/Core/Types/Neobook/NeobookLogin.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Neobook; + +public class NeobookLogin +{ + [JsonPropertyName("utoken")] public string Utoken { get; set; } + + [JsonPropertyName("uid")] public string Uid { get; set; } +} diff --git a/src/Core/Types/Neobook/NeobookPostData.cs b/src/Core/Types/Neobook/NeobookPostData.cs new file mode 100644 index 00000000..fb6c85f3 --- /dev/null +++ b/src/Core/Types/Neobook/NeobookPostData.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Neobook; + +public class NeobookPostData +{ + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("text")] public string Text { get; set; } + + [JsonPropertyName("user")] public NeobookUser User { get; set; } + + [JsonPropertyName("chapters")] public NeobookTocChapter[] Chapters { get; set; } + + [JsonPropertyName("attachment")] public NeobookAttachment Attachment { get; set; } +} diff --git a/src/Core/Types/Neobook/NeobookTocChapter.cs b/src/Core/Types/Neobook/NeobookTocChapter.cs new file mode 100644 index 00000000..e1540f65 --- /dev/null +++ b/src/Core/Types/Neobook/NeobookTocChapter.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Neobook; + +public class NeobookTocChapter +{ + [JsonPropertyName("token")] public string Token { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } +} diff --git a/src/Core/Types/Neobook/NeobookUser.cs b/src/Core/Types/Neobook/NeobookUser.cs new file mode 100644 index 00000000..3aacfef7 --- /dev/null +++ b/src/Core/Types/Neobook/NeobookUser.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Neobook; + +public class NeobookUser +{ + [JsonPropertyName("username")] public string UserName { get; set; } + + [JsonPropertyName("firstname")] public string FirstName { get; set; } + + [JsonPropertyName("lastname")] public string LastName { get; set; } +} diff --git a/src/Core/Types/Ranobe/RanobeBook.cs b/src/Core/Types/Ranobe/RanobeBook.cs new file mode 100644 index 00000000..1a1eea5d --- /dev/null +++ b/src/Core/Types/Ranobe/RanobeBook.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Ranobe; + +public class RanobeBook +{ + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("author")] public string Author { get; set; } + + [JsonPropertyName("verticalImages")] public RanobeImage[] Images { get; set; } + + [JsonPropertyName("verticalImage")] public RanobeImage Image { get; set; } + + [JsonPropertyName("chapters")] public List Chapters { get; set; } +} diff --git a/src/Core/Types/Ranobe/RanobeChapter.cs b/src/Core/Types/Ranobe/RanobeChapter.cs new file mode 100644 index 00000000..a2eb7fd4 --- /dev/null +++ b/src/Core/Types/Ranobe/RanobeChapter.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Ranobe; + +public class RanobeChapter +{ + [JsonPropertyName("content")] public RanobeChapterContent Content { get; set; } +} diff --git a/src/Core/Types/Ranobe/RanobeChapterContent.cs b/src/Core/Types/Ranobe/RanobeChapterContent.cs new file mode 100644 index 00000000..ae84c90c --- /dev/null +++ b/src/Core/Types/Ranobe/RanobeChapterContent.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Ranobe; + +public class RanobeChapterContent +{ + [JsonPropertyName("text")] public string Text { get; set; } +} diff --git a/src/Core/Types/Ranobe/RanobeChapterShort.cs b/src/Core/Types/Ranobe/RanobeChapterShort.cs new file mode 100644 index 00000000..eec594f9 --- /dev/null +++ b/src/Core/Types/Ranobe/RanobeChapterShort.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Ranobe; + +public class RanobeChapterShort +{ + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("url")] public string Url { get; set; } +} diff --git a/src/Core/Types/Ranobe/RanobeImage.cs b/src/Core/Types/Ranobe/RanobeImage.cs new file mode 100644 index 00000000..6f1e00c7 --- /dev/null +++ b/src/Core/Types/Ranobe/RanobeImage.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Ranobe; + +public class RanobeImage +{ + [JsonPropertyName("height")] public int Height { get; set; } + + [JsonPropertyName("url")] public string Url { get; set; } +} diff --git a/src/Core/Types/RanobeHub/RanobeHubApiResponse.cs b/src/Core/Types/RanobeHub/RanobeHubApiResponse.cs new file mode 100644 index 00000000..7f834b20 --- /dev/null +++ b/src/Core/Types/RanobeHub/RanobeHubApiResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.RanobeHub; + +public class RanobeHubApiResponse +{ + [JsonPropertyName("volumes")] public RanobeHubVolume[] Volumes { get; set; } +} diff --git a/src/Core/Types/RanobeHub/RanobeHubChapter.cs b/src/Core/Types/RanobeHub/RanobeHubChapter.cs new file mode 100644 index 00000000..c92d9904 --- /dev/null +++ b/src/Core/Types/RanobeHub/RanobeHubChapter.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.RanobeHub; + +public class RanobeHubChapter +{ + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("url")] public string Url { get; set; } +} diff --git a/src/Core/Types/RanobeHub/RanobeHubVolume.cs b/src/Core/Types/RanobeHub/RanobeHubVolume.cs new file mode 100644 index 00000000..d2131c9c --- /dev/null +++ b/src/Core/Types/RanobeHub/RanobeHubVolume.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.RanobeHub; + +public class RanobeHubVolume +{ + [JsonPropertyName("chapters")] public RanobeHubChapter[] Chapters { get; set; } +} diff --git a/src/Core/Types/RanobeNovels/RanobeNovelsChapter.cs b/src/Core/Types/RanobeNovels/RanobeNovelsChapter.cs new file mode 100644 index 00000000..f69cb4a5 --- /dev/null +++ b/src/Core/Types/RanobeNovels/RanobeNovelsChapter.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.RanobeNovels; + +public class RanobeNovelsChapter +{ + [JsonPropertyName("post_name")] public string Name { get; set; } + + [JsonPropertyName("post_title")] public string Title { get; set; } +} diff --git a/src/Core/Types/RanobeOvh/RanobeOvhBranch.cs b/src/Core/Types/RanobeOvh/RanobeOvhBranch.cs new file mode 100644 index 00000000..f9c1a255 --- /dev/null +++ b/src/Core/Types/RanobeOvh/RanobeOvhBranch.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.RanobeOvh; + +public class RanobeOvhBranch +{ + [JsonPropertyName("id")] public string Id { get; set; } + + [JsonPropertyName("translators")] public RanoveOvhTranslator[] Translators { get; set; } + + [JsonPropertyName("chaptersCount")] public long ChaptersCount { get; set; } +} diff --git a/src/Core/Types/RanobeOvh/RanobeOvhChapterFull.cs b/src/Core/Types/RanobeOvh/RanobeOvhChapterFull.cs new file mode 100644 index 00000000..6126c7cf --- /dev/null +++ b/src/Core/Types/RanobeOvh/RanobeOvhChapterFull.cs @@ -0,0 +1,56 @@ +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace Core.Types.RanobeOvh; + +public class RanobeOvhChapterFull +{ + [JsonPropertyName("pages")] public RanobeOvhPage[] Pages { get; set; } + + [JsonPropertyName("content")] public JsonNode Content { get; set; } +} + +public class RanobeOvhChapterShort +{ + [JsonPropertyName("id")] public string Id { get; set; } + + [JsonPropertyName("number")] public decimal Number { get; set; } + + [JsonPropertyName("volume")] public int? Volume { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("branchId")] public string BranchId { get; set; } + + public string FullName + { + get + { + if (!Volume.HasValue) + { + return Title; + } + + var shortName = $"Том {Volume}. Глава {(int)Number}"; + return string.IsNullOrWhiteSpace(Title) ? shortName : Title; + } + } +} + +public class RanobeOvhChapterContent +{ + [JsonPropertyName("type")] public string Type { get; set; } + + [JsonPropertyName("text")] public string Text { get; set; } + + [JsonPropertyName("marks")] public RanobeOvhChapterMark[] Marks { get; set; } = []; + + [JsonPropertyName("content")] public RanobeOvhChapterContent[] Content { get; set; } = []; + + [JsonPropertyName("attrs")] public Dictionary Attrs { get; set; } = new(); +} + +public class RanobeOvhChapterMark +{ + [JsonPropertyName("type")] public string Type { get; set; } +} diff --git a/src/Core/Types/RanobeOvh/RanobeOvhManga.cs b/src/Core/Types/RanobeOvh/RanobeOvhManga.cs new file mode 100644 index 00000000..dc8013ef --- /dev/null +++ b/src/Core/Types/RanobeOvh/RanobeOvhManga.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.RanobeOvh; + +public class RanobeOvhManga +{ + [JsonPropertyName("name")] public RanobeOvhMangaName Name { get; set; } + + [JsonPropertyName("poster")] public string Poster { get; set; } + + [JsonPropertyName("description")] public string Description { get; set; } +} diff --git a/src/Core/Types/RanobeOvh/RanobeOvhMangaName.cs b/src/Core/Types/RanobeOvh/RanobeOvhMangaName.cs new file mode 100644 index 00000000..6e3f66f6 --- /dev/null +++ b/src/Core/Types/RanobeOvh/RanobeOvhMangaName.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.RanobeOvh; + +public class RanobeOvhMangaName +{ + [JsonPropertyName("ru")] public string Ru { get; set; } +} diff --git a/src/Core/Types/RanobeOvh/RanobeOvhPage.cs b/src/Core/Types/RanobeOvh/RanobeOvhPage.cs new file mode 100644 index 00000000..4bf91bad --- /dev/null +++ b/src/Core/Types/RanobeOvh/RanobeOvhPage.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.RanobeOvh; + +public class RanobeOvhPage +{ + [JsonPropertyName("id")] public string Id { get; set; } + + [JsonPropertyName("image")] public string Image { get; set; } +} diff --git a/src/Core/Types/RanobeOvh/RanoveOvhTranslator.cs b/src/Core/Types/RanobeOvh/RanoveOvhTranslator.cs new file mode 100644 index 00000000..4f2c853b --- /dev/null +++ b/src/Core/Types/RanobeOvh/RanoveOvhTranslator.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.RanobeOvh; + +public class RanoveOvhTranslator +{ + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("slug")] public string Slug { get; set; } +} diff --git a/src/Core/Types/Ranobes/RanobesCookie.cs b/src/Core/Types/Ranobes/RanobesCookie.cs new file mode 100644 index 00000000..1603d3e8 --- /dev/null +++ b/src/Core/Types/Ranobes/RanobesCookie.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Ranobes; + +public class RanobesCookie +{ + [JsonPropertyName("cookie")] public string Cookie { get; set; } +} diff --git a/src/Core/Types/Renovels/RenovelsApiResponse.cs b/src/Core/Types/Renovels/RenovelsApiResponse.cs new file mode 100644 index 00000000..5053e6ec --- /dev/null +++ b/src/Core/Types/Renovels/RenovelsApiResponse.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Renovels; + +public class RenovelsApiResponse +{ + [JsonPropertyName("msg")] public string Message { get; set; } + + [JsonPropertyName("content")] public T Content { get; set; } +} diff --git a/src/Core/Types/Renovels/RenovelsAuthResponse.cs b/src/Core/Types/Renovels/RenovelsAuthResponse.cs new file mode 100644 index 00000000..e5157d59 --- /dev/null +++ b/src/Core/Types/Renovels/RenovelsAuthResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Renovels; + +public class RenovelsAuthResponse +{ + [JsonPropertyName("access_token")] public string Token { get; set; } +} diff --git a/src/Core/Types/Renovels/RenovelsBranch.cs b/src/Core/Types/Renovels/RenovelsBranch.cs new file mode 100644 index 00000000..350b6c3f --- /dev/null +++ b/src/Core/Types/Renovels/RenovelsBranch.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Renovels; + +public class RenovelsBranch +{ + [JsonPropertyName("id")] public int Id { get; set; } + + [JsonPropertyName("publishers")] public RenovelsPublisher[] Publishers { get; set; } +} diff --git a/src/Core/Types/Renovels/RenovelsChapter.cs b/src/Core/Types/Renovels/RenovelsChapter.cs new file mode 100644 index 00000000..741fedde --- /dev/null +++ b/src/Core/Types/Renovels/RenovelsChapter.cs @@ -0,0 +1,37 @@ +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace Core.Types.Renovels; + +public class RenovelsChapter +{ + [JsonPropertyName("id")] public int Id { get; set; } + + [JsonPropertyName("chapter")] public string Chapter { get; set; } + + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("tome")] public int Tome { get; set; } + + [JsonPropertyName("content")] public string Content { get; set; } + + [JsonPropertyName("is_paid")] public bool IsPaid { get; set; } + + [JsonPropertyName("is_bought")] public bool? IsBought { get; set; } + + [JsonPropertyName("pages")] public JsonArray Pages { get; set; } + + public string Title + { + get + { + var name = $"Том {Tome}. Глава {Chapter}"; + if (string.IsNullOrWhiteSpace(Name)) + { + return name; + } + + return name + $" - {Name}"; + } + } +} diff --git a/src/Core/Types/Renovels/RenovelsContent.cs b/src/Core/Types/Renovels/RenovelsContent.cs new file mode 100644 index 00000000..3521252a --- /dev/null +++ b/src/Core/Types/Renovels/RenovelsContent.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Renovels; + +public class RenovelsContent +{ + [JsonPropertyName("cover")] public Dictionary Cover { get; set; } + + [JsonPropertyName("main_name")] public string MainName { get; set; } + + [JsonPropertyName("secondary_name")] public string SecondaryName { get; set; } + + [JsonPropertyName("another_name")] public string AnotherName { get; set; } + + [JsonPropertyName("description")] public string Description { get; set; } + + [JsonPropertyName("branches")] public RenovelsBranch[] Branches { get; set; } +} diff --git a/src/Core/Types/Renovels/RenovelsPage.cs b/src/Core/Types/Renovels/RenovelsPage.cs new file mode 100644 index 00000000..eaa0c498 --- /dev/null +++ b/src/Core/Types/Renovels/RenovelsPage.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Renovels; + +public class RenovelsPage +{ + [JsonPropertyName("link")] public string Link { get; set; } +} diff --git a/src/Core/Types/Renovels/RenovelsPublisher.cs b/src/Core/Types/Renovels/RenovelsPublisher.cs new file mode 100644 index 00000000..dbb794f5 --- /dev/null +++ b/src/Core/Types/Renovels/RenovelsPublisher.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Renovels; + +public class RenovelsPublisher +{ + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("dir")] public string Dir { get; set; } +} diff --git a/src/Core/Types/Renovels/RenovelsTocResponse.cs b/src/Core/Types/Renovels/RenovelsTocResponse.cs new file mode 100644 index 00000000..d3b194f7 --- /dev/null +++ b/src/Core/Types/Renovels/RenovelsTocResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Renovels; + +public class RenovelsTocResponse +{ + [JsonPropertyName("results")] public RenovelsChapter[] Results { get; set; } +} diff --git a/src/Core/Types/Rulate/RulateAuthResponse.cs b/src/Core/Types/Rulate/RulateAuthResponse.cs new file mode 100644 index 00000000..8d1c48c0 --- /dev/null +++ b/src/Core/Types/Rulate/RulateAuthResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Rulate; + +public class RulateAuthResponse +{ + [JsonPropertyName("error")] public string Error { get; set; } +} diff --git a/src/Core/Types/Rulate/RulateChapter.cs b/src/Core/Types/Rulate/RulateChapter.cs new file mode 100644 index 00000000..ce64e866 --- /dev/null +++ b/src/Core/Types/Rulate/RulateChapter.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Rulate; + +public class RulateChapter +{ + [JsonPropertyName("can_read")] public bool CanRead { get; set; } + + [JsonPropertyName("content")] public string Content { get; set; } +} diff --git a/src/Core/Types/SocialLib/LibSocialToken.cs b/src/Core/Types/SocialLib/LibSocialToken.cs new file mode 100644 index 00000000..01b67789 --- /dev/null +++ b/src/Core/Types/SocialLib/LibSocialToken.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.SocialLib; + +public class LibSocialToken +{ + [JsonPropertyName("access_token")] public string AccessToken { get; set; } +} diff --git a/src/Core/Types/SocialLib/RanobeLibBookDetail.cs b/src/Core/Types/SocialLib/RanobeLibBookDetail.cs new file mode 100644 index 00000000..60f17bfd --- /dev/null +++ b/src/Core/Types/SocialLib/RanobeLibBookDetail.cs @@ -0,0 +1,42 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.SocialLib; + +public class RanobeLibBookDetails +{ + [JsonPropertyName("data")] public RLBDData Data { get; set; } +} + +public class RLBDData +{ + public RLBDData(int? id) + { + Id = id; + } + + [JsonPropertyName("id")] public int? Id { get; set; } + + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("rus_name")] public string RusName { get; set; } + + [JsonPropertyName("slug_url")] public string SlugUrl { get; set; } + + [JsonPropertyName("cover")] public RLBDCover Cover { get; set; } + + [JsonPropertyName("authors")] public List Authors { get; set; } + + [JsonPropertyName("summary")] public string Summary { get; set; } +} + +public class RLBDCover +{ + [JsonPropertyName("default")] public string Default { get; set; } +} + +public class RLBDAuthor +{ + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("slug_url")] public string SlugUrl { get; set; } +} diff --git a/src/Core/Types/SocialLib/SocialLibBookChapters.cs b/src/Core/Types/SocialLib/SocialLibBookChapters.cs new file mode 100644 index 00000000..c7b57e57 --- /dev/null +++ b/src/Core/Types/SocialLib/SocialLibBookChapters.cs @@ -0,0 +1,82 @@ +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace Core.Types.SocialLib; + +public class SocialLibBookChapters +{ + [JsonPropertyName("data")] public List Chapters { get; set; } +} + +public class SocialLibBookChapterResponse +{ + [JsonPropertyName("data")] public SocialLibBookChapter Data { get; set; } +} + +public class SocialLibChapterBranch +{ + [JsonPropertyName("branch_id")] public long? BranchId { get; set; } +} + +public class SocialLibChapterAttachment +{ + [JsonPropertyName("url")] public string Url { get; set; } + + [JsonPropertyName("name")] public string Name { get; set; } +} + +public class SocialLibBookChapter +{ + [JsonPropertyName("volume")] public string Volume { get; set; } + + [JsonPropertyName("number")] public string Number { get; set; } + + [JsonPropertyName("branches")] public List Branches { get; set; } + + [JsonPropertyName("attachments")] public SocialLibChapterAttachment[] Attachments { get; set; } = []; + + [JsonPropertyName("pages")] public SocialLibPage[] Pages { get; set; } = []; + + [JsonPropertyName("content")] public JsonNode Content { get; set; } + + private string RawName { get; set; } + + [JsonPropertyName("name")] + public string Name + { + get + { + var name = $"Том {Volume} Глава {Number}"; + if (!string.IsNullOrWhiteSpace(RawName)) + { + name += $" - {RawName}"; + } + + return name; + } + set { RawName = value; } + } +} + +public class SocialLibChapterContent +{ + [JsonPropertyName("type")] public string Type { get; set; } + + [JsonPropertyName("text")] public string Text { get; set; } + + [JsonPropertyName("marks")] public SocialLibChapterMark[] Marks { get; set; } = []; + + [JsonPropertyName("content")] public SocialLibChapterContent[] Content { get; set; } = []; + + [JsonPropertyName("attrs")] public Dictionary Attrs { get; set; } = new(); +} + +public class SocialLibChapterMark +{ + [JsonPropertyName("type")] public string Type { get; set; } +} + +public class SocialLibPage +{ + [JsonPropertyName("url")] public string Url { get; set; } +} diff --git a/src/Core/Types/SocialLib/SocialLibChapter.cs b/src/Core/Types/SocialLib/SocialLibChapter.cs new file mode 100644 index 00000000..7473fe0a --- /dev/null +++ b/src/Core/Types/SocialLib/SocialLibChapter.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.SocialLib; + +public class SocialLibChapter +{ + [JsonPropertyName("chapter_number")] public string ChapterNumber { get; set; } + + [JsonPropertyName("chapter_volume")] public int ChapterVolume { get; set; } + + [JsonPropertyName("chapter_name")] public string ChapterName { get; set; } + + [JsonPropertyName("chapter_slug")] public string ChapterSlug { get; set; } + + [JsonPropertyName("branch_id")] public int? BranchId { get; set; } + + public string Name => $"Том {ChapterVolume} Глава {ChapterNumber} {ChapterName}".Trim(); +} diff --git a/src/Core/Types/SocialLib/SocialLibChapterOld.cs b/src/Core/Types/SocialLib/SocialLibChapterOld.cs new file mode 100644 index 00000000..039797af --- /dev/null +++ b/src/Core/Types/SocialLib/SocialLibChapterOld.cs @@ -0,0 +1,19 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.SocialLib; + +public class SocialLibChapterOld +{ + [JsonPropertyName("number")] public string ChapterNumber { get; set; } + + [JsonPropertyName("volume")] public string ChapterVolume { get; set; } + + [JsonPropertyName("name")] public string ChapterName { get; set; } + + [JsonPropertyName("slug")] public string ChapterSlug { get; set; } + + public string GetName() + { + return $"Том {ChapterVolume} Глава {ChapterNumber} {ChapterName}".Trim(); + } +} diff --git a/src/Core/Types/SocialLib/SocialLibChapters.cs b/src/Core/Types/SocialLib/SocialLibChapters.cs new file mode 100644 index 00000000..ade446c4 --- /dev/null +++ b/src/Core/Types/SocialLib/SocialLibChapters.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.SocialLib; + +public class SocialLibChapters +{ + [JsonPropertyName("list")] public List List { get; set; } +} diff --git a/src/Core/Types/SocialLib/User.cs b/src/Core/Types/SocialLib/User.cs new file mode 100644 index 00000000..5cc19a89 --- /dev/null +++ b/src/Core/Types/SocialLib/User.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.SocialLib; + +public class User +{ + [JsonPropertyName("id")] public long Id { get; set; } +} diff --git a/src/Core/Types/SocialLib/WindowData.cs b/src/Core/Types/SocialLib/WindowData.cs new file mode 100644 index 00000000..a6cc2dff --- /dev/null +++ b/src/Core/Types/SocialLib/WindowData.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.SocialLib; + +public class WindowData +{ + [JsonPropertyName("chapters")] public SocialLibChapters Chapters { get; set; } + + [JsonPropertyName("user")] public User User { get; set; } +} diff --git a/src/Core/Types/StrokiMts/StrokiMtsApiMultiResponse.cs b/src/Core/Types/StrokiMts/StrokiMtsApiMultiResponse.cs new file mode 100644 index 00000000..f3132490 --- /dev/null +++ b/src/Core/Types/StrokiMts/StrokiMtsApiMultiResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.StrokiMts; + +public class StrokiMtsApiMultiResponse +{ + [JsonPropertyName("items")] public List Items { get; set; } +} diff --git a/src/Core/Types/StrokiMts/StrokiMtsApiResponse.cs b/src/Core/Types/StrokiMts/StrokiMtsApiResponse.cs new file mode 100644 index 00000000..219c82fd --- /dev/null +++ b/src/Core/Types/StrokiMts/StrokiMtsApiResponse.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.StrokiMts; + +public class StrokiMtsApiResponse +{ + [JsonPropertyName("data")] public T Data { get; set; } +} diff --git a/src/Core/Types/StrokiMts/StrokiMtsAuthor.cs b/src/Core/Types/StrokiMts/StrokiMtsAuthor.cs new file mode 100644 index 00000000..3e91280d --- /dev/null +++ b/src/Core/Types/StrokiMts/StrokiMtsAuthor.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.StrokiMts; + +public class StrokiMtsAuthor +{ + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("friendlyUrl")] public string FriendlyUrl { get; set; } +} diff --git a/src/Core/Types/StrokiMts/StrokiMtsBookItem.cs b/src/Core/Types/StrokiMts/StrokiMtsBookItem.cs new file mode 100644 index 00000000..f6eff794 --- /dev/null +++ b/src/Core/Types/StrokiMts/StrokiMtsBookItem.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.StrokiMts; + +public class StrokiMtsBookItem +{ + [JsonPropertyName("id")] public long Id { get; set; } + + [JsonPropertyName("annotation")] public string Annotation { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } + + [JsonPropertyName("imageUrl")] public Dictionary ImageUrl { get; set; } + + [JsonPropertyName("authors")] public List Authors { get; set; } + + [JsonPropertyName("contentInfo")] public StrokiMtsContentInfo ContentInfo { get; set; } +} diff --git a/src/Core/Types/StrokiMts/StrokiMtsContentInfo.cs b/src/Core/Types/StrokiMts/StrokiMtsContentInfo.cs new file mode 100644 index 00000000..b838d12f --- /dev/null +++ b/src/Core/Types/StrokiMts/StrokiMtsContentInfo.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.StrokiMts; + +public class StrokiMtsContentInfo +{ + [JsonPropertyName("fileType")] public string FileType { get; set; } +} diff --git a/src/Core/Types/StrokiMts/StrokiMtsFile.cs b/src/Core/Types/StrokiMts/StrokiMtsFile.cs new file mode 100644 index 00000000..f2da4ce2 --- /dev/null +++ b/src/Core/Types/StrokiMts/StrokiMtsFile.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.StrokiMts; + +public class StrokiMtsFile +{ + [JsonPropertyName("fileId")] public long FileId { get; set; } +} diff --git a/src/Core/Types/StrokiMts/StrokiMtsFileUrl.cs b/src/Core/Types/StrokiMts/StrokiMtsFileUrl.cs new file mode 100644 index 00000000..b111fca1 --- /dev/null +++ b/src/Core/Types/StrokiMts/StrokiMtsFileUrl.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.StrokiMts; + +public class StrokiMtsFileUrl +{ + [JsonPropertyName("url")] public string Url { get; set; } +} diff --git a/src/Core/Types/StrokiMts/StrokiMtsFiles.cs b/src/Core/Types/StrokiMts/StrokiMtsFiles.cs new file mode 100644 index 00000000..01fa6236 --- /dev/null +++ b/src/Core/Types/StrokiMts/StrokiMtsFiles.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.StrokiMts; + +public class StrokiMtsFiles +{ + [JsonPropertyName("preview")] public StrokiMtsFile Preview { get; set; } + + [JsonPropertyName("full")] public StrokiMtsFile[] Full { get; set; } +} diff --git a/src/Core/Types/StrokiMts/StrokiMtsMultiItem.cs b/src/Core/Types/StrokiMts/StrokiMtsMultiItem.cs new file mode 100644 index 00000000..5d2502a8 --- /dev/null +++ b/src/Core/Types/StrokiMts/StrokiMtsMultiItem.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.StrokiMts; + +public class StrokiMtsMultiItem +{ + [JsonPropertyName("textBook")] public StrokiMtsBookItem TextBook { get; set; } + + [JsonPropertyName("audioBook")] public StrokiMtsBookItem AudioBook { get; set; } + + [JsonPropertyName("comics")] public StrokiMtsBookItem ComicBook { get; set; } + + [JsonPropertyName("pressa")] public StrokiMtsBookItem PressBook { get; set; } +} diff --git a/src/Core/Types/Wattpad/WattpadInfo.cs b/src/Core/Types/Wattpad/WattpadInfo.cs new file mode 100644 index 00000000..1ae50bf8 --- /dev/null +++ b/src/Core/Types/Wattpad/WattpadInfo.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Wattpad; + +public class WattpadInfo +{ + [JsonPropertyName("user")] public WattpadUser User { get; set; } + + [JsonPropertyName("cover")] public string Cover { get; set; } + + [JsonPropertyName("parts")] public WattpadPart[] Parts { get; set; } + + [JsonPropertyName("description")] public string Description { get; set; } + + [JsonPropertyName("title")] public string Title { get; set; } +} diff --git a/src/Core/Types/Wattpad/WattpadMeta.cs b/src/Core/Types/Wattpad/WattpadMeta.cs new file mode 100644 index 00000000..13f106f6 --- /dev/null +++ b/src/Core/Types/Wattpad/WattpadMeta.cs @@ -0,0 +1,6 @@ +namespace Core.Types.Wattpad; + +public class WattpadMeta +{ + public string StoryId { get; set; } +} diff --git a/Core/Types/Wattpad/WattpadPart.cs b/src/Core/Types/Wattpad/WattpadPart.cs similarity index 51% rename from Core/Types/Wattpad/WattpadPart.cs rename to src/Core/Types/Wattpad/WattpadPart.cs index e9995f32..3d62d68b 100644 --- a/Core/Types/Wattpad/WattpadPart.cs +++ b/src/Core/Types/Wattpad/WattpadPart.cs @@ -1,17 +1,15 @@ -using System; using System.Text.Json.Serialization; using Core.Extensions; -namespace Core.Types.Wattpad; +namespace Core.Types.Wattpad; -public class WattpadPart { - [JsonPropertyName("id")] - public long Id { get; set; } - - [JsonPropertyName("title")] - public string Title { private get; set; } +public class WattpadPart +{ + [JsonPropertyName("id")] public long Id { get; set; } + + [JsonPropertyName("title")] public string Title { private get; set; } public string FullName => Title.Replace("\r", " ").CollapseWhitespace().Trim(); public Uri Url => new($"https://www.wattpad.com/apiv2/storytext?id={Id}"); -} \ No newline at end of file +} diff --git a/src/Core/Types/Wattpad/WattpadUser.cs b/src/Core/Types/Wattpad/WattpadUser.cs new file mode 100644 index 00000000..f5990de3 --- /dev/null +++ b/src/Core/Types/Wattpad/WattpadUser.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.Wattpad; + +public class WattpadUser +{ + [JsonPropertyName("name")] public string Name { get; set; } +} diff --git a/src/Core/Types/WuxiaWorld/WuxiaWorldToc.cs b/src/Core/Types/WuxiaWorld/WuxiaWorldToc.cs new file mode 100644 index 00000000..c814037c --- /dev/null +++ b/src/Core/Types/WuxiaWorld/WuxiaWorldToc.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Core.Types.WuxiaWorld; + +public class WuxiaWorldToc +{ + [JsonPropertyName("post_name")] public string PostName { get; set; } +} diff --git a/src/Elib2EbookCli/ConsoleLogger.cs b/src/Elib2EbookCli/ConsoleLogger.cs new file mode 100644 index 00000000..542f4a13 --- /dev/null +++ b/src/Elib2EbookCli/ConsoleLogger.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Logging; + +namespace Elib2EbookCli; + +public class ConsoleLogger : ILogger +{ + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + Console.WriteLine($"{formatter(state, exception)}"); + } + + bool ILogger.IsEnabled(LogLevel logLevel) + { + return true; + } + + public IDisposable BeginScope(TState state) where TState : notnull + => null!; +} diff --git a/Elib2EbookCli/Dockerfile b/src/Elib2EbookCli/Dockerfile similarity index 63% rename from Elib2EbookCli/Dockerfile rename to src/Elib2EbookCli/Dockerfile index 030092aa..73d5ce32 100644 --- a/Elib2EbookCli/Dockerfile +++ b/src/Elib2EbookCli/Dockerfile @@ -2,8 +2,8 @@ WORKDIR /app COPY . . -RUN dotnet restore -RUN dotnet publish -c Release -o /app/publish +RUN dotnet restore Elib2EbookCli/Elib2EbookCli.csproj +RUN dotnet publish Elib2EbookCli/Elib2EbookCli.csproj -c Release -o /app/publish FROM mcr.microsoft.com/dotnet/aspnet:9.0 WORKDIR /app diff --git a/src/Elib2EbookCli/Elib2EbookCli.csproj b/src/Elib2EbookCli/Elib2EbookCli.csproj new file mode 100644 index 00000000..1d46e38c --- /dev/null +++ b/src/Elib2EbookCli/Elib2EbookCli.csproj @@ -0,0 +1,21 @@ + + + + Exe + + + + + + + + + + + + + .dockerignore + + + + diff --git a/Elib2EbookCli/Program.cs b/src/Elib2EbookCli/Program.cs similarity index 62% rename from Elib2EbookCli/Program.cs rename to src/Elib2EbookCli/Program.cs index 7e1b7917..9a6fa6d9 100644 --- a/Elib2EbookCli/Program.cs +++ b/src/Elib2EbookCli/Program.cs @@ -10,69 +10,90 @@ namespace Elib2EbookCli; -internal static class Program { - - private static async Task Main(string[] args) { +internal static class Program +{ + private static async Task Main(string[] args) + { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); Console.OutputEncoding = Encoding.UTF8; - + var logger = new ConsoleLogger(); - var parserResult = new Parser(c => { + var parserResult = new Parser(c => + { c.CaseInsensitiveEnumValues = true; }).ParseArguments(args); - - + await parserResult - .WithNotParsed(errs => { + .WithNotParsed(errs => + { var title = AppDomain.CurrentDomain.FriendlyName; - var coreAssembly = typeof(BookGetterConfig).Assembly; + var coreAssembly = typeof(BookGetterConfig).Assembly; var version = coreAssembly.GetCustomAttribute()?.Version ?? string.Empty; var heading = new HeadingInfo(title, version); - if (errs.IsVersion()) { + if (errs.IsVersion()) + { logger.LogInformation(heading); - } else { - var helpText = HelpText.AutoBuild(parserResult, h => { - h.Heading = heading; - h.Copyright = string.Empty; - return h; - }); - + } + else + { + var helpText = HelpText.AutoBuild( + parserResult, + h => + { + h.Heading = heading; + h.Copyright = string.Empty; + return h; + }); + logger.LogInformation(helpText); } }) - .WithParsedAsync(async options => { - using var getterConfig = BookGetterConfig.GetDefault(options, logger); + .WithParsedAsync(async options => + { + using var getterConfig = BookGetterConfig.GetDefault(options, logger); using var getter = GetterProvider.Get(getterConfig, options.Url.First().AsUri()); - try { + try + { await getter.Init(); await getter.Authorize(); - } catch (Exception ex) { + } + catch (Exception ex) + { logger.LogInformation($"Ошибка авторизации или инициализации. {ex.Message}"); return; } - foreach (var url in options.Url) { + foreach (var url in options.Url) + { logger.LogInformation($"Начинаю генерацию книги {url.CoverQuotes()}"); - try { + try + { var book = await getter.Get(url.AsUri()); - foreach (var format in options.Format) { + foreach (var format in options.Format) + { await BuilderProvider.Get(format, options, logger).Build(book); } - if (options.Additional) { + if (options.Additional) + { await new AdditionaFileBuilder(options, logger).Build(book); } - if (!options.SaveTemp) { + if (!options.SaveTemp) + { book.Dispose(); } - } catch (TaskCanceledException) { + } + catch (TaskCanceledException) + { logger.LogInformation("Сервер не успевает ответить. Попробуйте увеличить Timeout с помощью параметра -t"); - } catch (Exception ex) { + } + catch (Exception ex) + { logger.LogInformation($"Генерация книги {url} завершилась с ошибкой. {ex}"); } } diff --git a/src/Elib2EbookWeb/Components/App.razor b/src/Elib2EbookWeb/Components/App.razor new file mode 100644 index 00000000..ddbe6a53 --- /dev/null +++ b/src/Elib2EbookWeb/Components/App.razor @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Elib2EbookWeb/Components/BookForm.razor b/src/Elib2EbookWeb/Components/BookForm.razor similarity index 62% rename from Elib2EbookWeb/Components/BookForm.razor rename to src/Elib2EbookWeb/Components/BookForm.razor index a438ee70..f2206e06 100644 --- a/Elib2EbookWeb/Components/BookForm.razor +++ b/src/Elib2EbookWeb/Components/BookForm.razor @@ -6,28 +6,42 @@ - + - - - - - + + + + + - + - - - - @if (_startEndCSwitcher) { + + + + @if (_startEndCSwitcher) + {
- } else { + } + else + {
@@ -42,17 +56,25 @@ - @if (_onGenerate) { - - @if (_onGenerate) { - @if (!string.IsNullOrWhiteSpace(_log)) { + @if (_onGenerate) + { + + @if (_onGenerate) + { + @if (!string.IsNullOrWhiteSpace(_log)) + { @_log - } else { + } + else + { } } - } else { + } + else + { Скачать @@ -66,68 +88,78 @@ @code { private readonly Options _options = new(); - private ActionLogger? _logger; + private ActionLogger _logger; private bool _onGenerate; - private string? _log; + private string _log; private bool _startEndCSwitcher; - + private bool _isShow; private InputType _passwordInput = InputType.Password; private string _passwordInputIcon = Icons.Material.Filled.VisibilityOff; - [Parameter] - public required Func OnClick { get; set; } + [Parameter] public required Func OnClick { get; set; } - void ShowPassword() { - if (_isShow) { + void ShowPassword() + { + if (_isShow) + { _isShow = false; _passwordInputIcon = Icons.Material.Filled.VisibilityOff; _passwordInput = InputType.Password; - } else { + } + else + { _isShow = true; _passwordInputIcon = Icons.Material.Filled.Visibility; _passwordInput = InputType.Text; } } - private async Task WithImages() { + private async Task WithImages() + { _options.NoImage = false; await Download(); } - - private async Task NoImages() { + + private async Task NoImages() + { _options.NoImage = true; await Download(); } - private async Task Download() { - if (_options.Url == null || !_options.Url.Any() || string.IsNullOrWhiteSpace(_options.Url.First())) { + private async Task Download() + { + if (_options.Url == null || !_options.Url.Any() || string.IsNullOrWhiteSpace(_options.Url.First())) + { Snackbar.Add("Укажите ссылку на книгу", Severity.Error); return; } - - if (_options.Format == null || !_options.Format.Any()) { + + if (_options.Format == null || !_options.Format.Any()) + { Snackbar.Add("Выберите формат", Severity.Error); return; } - + _onGenerate = true; - _logger = new ActionLogger(str => { + _logger = new ActionLogger(str => + { _log = str.Crop(300); InvokeAsync(StateHasChanged); }); - + await OnClick(_options, _logger); _onGenerate = false; } - private void OnSwitch(bool obj) { + private void OnSwitch(bool obj) + { _startEndCSwitcher = obj; - - _options.Start = default; - _options.End = default; - _options.StartName = default; - _options.EndName = default; + + _options.Start = null; + _options.End = null; + _options.StartName = null; + _options.EndName = null; } - -} \ No newline at end of file + +} diff --git a/Elib2EbookWeb/Components/Layout/MainLayout.razor b/src/Elib2EbookWeb/Components/Layout/MainLayout.razor similarity index 62% rename from Elib2EbookWeb/Components/Layout/MainLayout.razor rename to src/Elib2EbookWeb/Components/Layout/MainLayout.razor index 52b2136f..92d43a9d 100644 --- a/Elib2EbookWeb/Components/Layout/MainLayout.razor +++ b/src/Elib2EbookWeb/Components/Layout/MainLayout.razor @@ -1,20 +1,21 @@ @inherits LayoutComponentBase - - - + + +
Elib2Ebook - - - + + + - +
@Body
@@ -25,4 +26,4 @@ An unhandled error has occurred. Reload 🗙 -
\ No newline at end of file +
diff --git a/Elib2EbookWeb/Components/Layout/MainLayout.razor.css b/src/Elib2EbookWeb/Components/Layout/MainLayout.razor.css similarity index 72% rename from Elib2EbookWeb/Components/Layout/MainLayout.razor.css rename to src/Elib2EbookWeb/Components/Layout/MainLayout.razor.css index 9c4a0f37..d49e4b7e 100644 --- a/Elib2EbookWeb/Components/Layout/MainLayout.razor.css +++ b/src/Elib2EbookWeb/Components/Layout/MainLayout.razor.css @@ -21,20 +21,20 @@ main { align-items: center; } - .top-row ::deep a, .top-row ::deep .btn-link { - white-space: nowrap; - margin-left: 1.5rem; - text-decoration: none; - } +.top-row ::deep a, .top-row ::deep .btn-link { + white-space: nowrap; + margin-left: 1.5rem; + text-decoration: none; +} - .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { - text-decoration: underline; - } +.top-row ::deep a:hover, .top-row ::deep .btn-link:hover { + text-decoration: underline; +} - .top-row ::deep a:first-child { - overflow: hidden; - text-overflow: ellipsis; - } +.top-row ::deep a:first-child { + overflow: hidden; + text-overflow: ellipsis; +} @media (max-width: 640.98px) { .top-row { @@ -88,9 +88,9 @@ main { z-index: 1000; } - #blazor-error-ui .dismiss { - cursor: pointer; - position: absolute; - right: 0.75rem; - top: 0.5rem; - } \ No newline at end of file +#blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; +} diff --git a/src/Elib2EbookWeb/Components/PWA.razor b/src/Elib2EbookWeb/Components/PWA.razor new file mode 100644 index 00000000..d971a9d0 --- /dev/null +++ b/src/Elib2EbookWeb/Components/PWA.razor @@ -0,0 +1,32 @@ +Установить приложение + + + diff --git a/Elib2EbookWeb/Components/Pages/Error.razor b/src/Elib2EbookWeb/Components/Pages/Error.razor similarity index 67% rename from Elib2EbookWeb/Components/Pages/Error.razor rename to src/Elib2EbookWeb/Components/Pages/Error.razor index 86dea8a6..bae7cf02 100644 --- a/Elib2EbookWeb/Components/Pages/Error.razor +++ b/src/Elib2EbookWeb/Components/Pages/Error.razor @@ -6,7 +6,8 @@

Error.

An error occurred while processing your request.

-@if (ShowRequestId) { +@if (ShowRequestId) +{

Request ID: @RequestId

@@ -14,22 +15,25 @@

Development Mode

- Swapping to Development environment will display more detailed information about the error that occurred. + Swapping to Development environment will display more detailed information about the error that + occurred.

The Development environment shouldn't be enabled for deployed applications. It can result in displaying sensitive information from exceptions to end users. - For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT + environment variable to Development and restarting the app.

@code{ - [CascadingParameter] - private HttpContext? HttpContext { get; set; } + [CascadingParameter] private HttpContext HttpContext { get; set; } - private string? RequestId { get; set; } + private string RequestId { get; set; } private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - protected override void OnInitialized() => RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; -} \ No newline at end of file + protected override void OnInitialized() + => RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; + +} diff --git a/Elib2EbookWeb/Components/Pages/Home.razor b/src/Elib2EbookWeb/Components/Pages/Home.razor similarity index 81% rename from Elib2EbookWeb/Components/Pages/Home.razor rename to src/Elib2EbookWeb/Components/Pages/Home.razor index 6ec407ca..372132a1 100644 --- a/Elib2EbookWeb/Components/Pages/Home.razor +++ b/src/Elib2EbookWeb/Components/Pages/Home.razor @@ -16,7 +16,8 @@ -@if (_shortBook != default) { +@if (_shortBook != null) +{ @@ -26,23 +27,27 @@ @code { - private Book? _shortBook; - - private async Task OnClick(Options options, ILogger logger) { + private Book _shortBook; + + private async Task OnClick(Options options, ILogger logger) + { using var tempSave = TempFolderFactory.Create("Books_" + Guid.NewGuid()); options.Timeout = 10; options.SavePath = tempSave.Path; - try { + try + { using var getterConfig = BookGetterConfig.GetDefault(options, logger); using var getter = GetterProvider.Get(getterConfig, options.Url.First().AsUri()); - + await getter.Init(); await getter.Authorize(); - foreach (var url in options.Url) { - try { + foreach (var url in options.Url) + { + try + { getterConfig.Options.NoChapters = true; _shortBook = await getter.Get(url.AsUri()); @@ -50,29 +55,37 @@ getterConfig.Options.NoChapters = false; using var book = await getter.Get(url.AsUri()); - foreach (var format in options.Format) { + foreach (var format in options.Format) + { await BuilderProvider.Get(format, options, logger).Build(book); } - + logger.LogInformation("Ожидайте. Сейчас начнется скачивание"); - foreach (var fileName in Directory.GetFiles(tempSave.Path)) { + foreach (var fileName in Directory.GetFiles(tempSave.Path)) + { await using var file = File.OpenRead(fileName); using var streamRef = new DotNetStreamReference(file); await JsRuntime.InvokeVoidAsync("downloadFileFromStream", Path.GetFileName(file.Name), streamRef); } Snackbar.Add($"Генерация книги {GetBookName(url, _shortBook).CoverQuotes()} завершена", Severity.Success); - } catch (Exception ex) { + } + catch (Exception ex) + { logger.LogInformation($"Генерация книги {GetBookName(url, _shortBook).CoverQuotes()} завершилась с ошибкой. {ex}"); Snackbar.Add($"Генерация книги {GetBookName(url, _shortBook).CoverQuotes()} завершилась с ошибкой. {ex.Message}", Severity.Error); } } - } catch (Exception ex) { + } + catch (Exception ex) + { logger.LogInformation(ex.Message); Snackbar.Add(ex.Message, Severity.Error); } } - private string GetBookName(string url, Book? book) => book?.Title ?? url; -} \ No newline at end of file + private static string GetBookName(string url, Book book) + => book?.Title ?? url; + +} diff --git a/Elib2EbookWeb/Components/Routes.razor b/src/Elib2EbookWeb/Components/Routes.razor similarity index 100% rename from Elib2EbookWeb/Components/Routes.razor rename to src/Elib2EbookWeb/Components/Routes.razor diff --git a/src/Elib2EbookWeb/Components/ShortBook.razor b/src/Elib2EbookWeb/Components/ShortBook.razor new file mode 100644 index 00000000..2f85581c --- /dev/null +++ b/src/Elib2EbookWeb/Components/ShortBook.razor @@ -0,0 +1,83 @@ +@using Core.Types.Book + + + + + + + + + + @Book.Title + + + @if (Book.Author != null) + { + @if (Book.Author.Url == null) + { + Автор: @Book.Author.Name + } + else + { + Автор: + @Book.Author.Name + + } + } + + @if (Book.Seria != null) + { + @if (Book.Seria?.Url == null) + { + Серия: @Seria + } + else + { + Серия: + @Seria + + } + } + + @if (!string.IsNullOrWhiteSpace(Book.Annotation)) + { + @((MarkupString)Book.Annotation) + } + + + + + +@code { + [Parameter] public required Book Book { get; set; } + + private string _cover; + + private string _coverPath; + + private string Seria => Book.Seria?.Name + (string.IsNullOrWhiteSpace(Book.Seria?.Number) ? string.Empty : $" #{Book.Seria.Number}"); + + protected override Task OnInitializedAsync() + { + InitCover(); + return base.OnInitializedAsync(); + } + + protected override Task OnParametersSetAsync() + { + if (Book.Cover?.FilePath != _coverPath) + { + InitCover(); + } + + return base.OnParametersSetAsync(); + } + + private void InitCover() + { + _cover = Book.Cover != null && Book.Cover.Content.Length > 0 ? $"data:image/png;base64, {Convert.ToBase64String(Book.Cover.Content)}" : _cover; + _coverPath = Book.Cover?.FilePath; + } + +} diff --git a/Elib2EbookWeb/Components/_Imports.razor b/src/Elib2EbookWeb/Components/_Imports.razor similarity index 100% rename from Elib2EbookWeb/Components/_Imports.razor rename to src/Elib2EbookWeb/Components/_Imports.razor diff --git a/Elib2EbookWeb/Dockerfile b/src/Elib2EbookWeb/Dockerfile similarity index 62% rename from Elib2EbookWeb/Dockerfile rename to src/Elib2EbookWeb/Dockerfile index 3ef9cf8d..48bcc99b 100644 --- a/Elib2EbookWeb/Dockerfile +++ b/src/Elib2EbookWeb/Dockerfile @@ -4,8 +4,8 @@ EXPOSE 8080 EXPOSE 8081 COPY . . -RUN dotnet restore -RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false +RUN dotnet restore Elib2EbookWeb/Elib2EbookWeb.csproj +RUN dotnet publish Elib2EbookWeb/Elib2EbookWeb.csproj -c Release -o /app/publish /p:UseAppHost=false FROM mcr.microsoft.com/dotnet/aspnet:9.0 WORKDIR /app diff --git a/src/Elib2EbookWeb/Elib2EbookWeb.csproj b/src/Elib2EbookWeb/Elib2EbookWeb.csproj new file mode 100644 index 00000000..41d88c41 --- /dev/null +++ b/src/Elib2EbookWeb/Elib2EbookWeb.csproj @@ -0,0 +1,19 @@ + + + + + + + + + + + + .dockerignore + + + PreserveNewest + + + + diff --git a/src/Elib2EbookWeb/Misc/SbWriter.cs b/src/Elib2EbookWeb/Misc/SbWriter.cs new file mode 100644 index 00000000..99e9522d --- /dev/null +++ b/src/Elib2EbookWeb/Misc/SbWriter.cs @@ -0,0 +1,22 @@ +namespace Elib2EbookWeb.Misc; + +public class ActionLogger : ILogger +{ + private readonly Action _action; + + public ActionLogger(Action action) + { + _action = action; + } + + public IDisposable BeginScope(TState state) where TState : notnull + => null!; + + public bool IsEnabled(LogLevel logLevel) + => true; + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + _action($"{formatter(state, exception)}"); + } +} diff --git a/Elib2EbookWeb/Program.cs b/src/Elib2EbookWeb/Program.cs similarity index 92% rename from Elib2EbookWeb/Program.cs rename to src/Elib2EbookWeb/Program.cs index 500706e3..90cb2701 100644 --- a/Elib2EbookWeb/Program.cs +++ b/src/Elib2EbookWeb/Program.cs @@ -6,7 +6,8 @@ // Add services to the container. builder.Services.AddRazorComponents().AddInteractiveServerComponents(); -builder.Services.AddMudServices(config => { +builder.Services.AddMudServices(config => +{ config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomCenter; config.SnackbarConfiguration.PreventDuplicates = false; @@ -21,7 +22,8 @@ var app = builder.Build(); // Configure the HTTP request pipeline. -if (!app.Environment.IsDevelopment()) { +if (!app.Environment.IsDevelopment()) +{ app.UseExceptionHandler("/Error", createScopeForErrors: true); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); @@ -35,4 +37,4 @@ app.MapRazorComponents() .AddInteractiveServerRenderMode(); -app.Run(); \ No newline at end of file +app.Run(); diff --git a/src/Elib2EbookWeb/Properties/launchSettings.json b/src/Elib2EbookWeb/Properties/launchSettings.json new file mode 100644 index 00000000..e0d10891 --- /dev/null +++ b/src/Elib2EbookWeb/Properties/launchSettings.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:41898", + "sslPort": 44383 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5271", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7188;http://localhost:5271", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Elib2EbookWeb/appsettings.Development.json b/src/Elib2EbookWeb/appsettings.Development.json similarity index 100% rename from Elib2EbookWeb/appsettings.Development.json rename to src/Elib2EbookWeb/appsettings.Development.json diff --git a/Elib2EbookWeb/appsettings.json b/src/Elib2EbookWeb/appsettings.json similarity index 100% rename from Elib2EbookWeb/appsettings.json rename to src/Elib2EbookWeb/appsettings.json diff --git a/Elib2EbookWeb/wwwroot/app.css b/src/Elib2EbookWeb/wwwroot/app.css similarity index 94% rename from Elib2EbookWeb/wwwroot/app.css rename to src/Elib2EbookWeb/wwwroot/app.css index 5dfd85f2..98afec1c 100644 --- a/Elib2EbookWeb/wwwroot/app.css +++ b/src/Elib2EbookWeb/wwwroot/app.css @@ -13,7 +13,7 @@ a, .btn-link { } .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { - box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; + box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; } .content { @@ -42,9 +42,9 @@ h1:focus { color: white; } - .blazor-error-boundary::after { - content: "An error has occurred." - } +.blazor-error-boundary::after { + content: "An error has occurred." +} .darker-border-checkbox.form-check-input { border-color: #929292; @@ -55,4 +55,4 @@ h1:focus { max-height: 100%; display: flex; flex-direction: column-reverse; -} \ No newline at end of file +} diff --git a/Elib2EbookWeb/wwwroot/bootstrap/bootstrap.min.css b/src/Elib2EbookWeb/wwwroot/bootstrap/bootstrap.min.css similarity index 100% rename from Elib2EbookWeb/wwwroot/bootstrap/bootstrap.min.css rename to src/Elib2EbookWeb/wwwroot/bootstrap/bootstrap.min.css diff --git a/Elib2EbookWeb/wwwroot/bootstrap/bootstrap.min.css.map b/src/Elib2EbookWeb/wwwroot/bootstrap/bootstrap.min.css.map similarity index 100% rename from Elib2EbookWeb/wwwroot/bootstrap/bootstrap.min.css.map rename to src/Elib2EbookWeb/wwwroot/bootstrap/bootstrap.min.css.map diff --git a/Elib2EbookWeb/wwwroot/favicon.png b/src/Elib2EbookWeb/wwwroot/favicon.png similarity index 100% rename from Elib2EbookWeb/wwwroot/favicon.png rename to src/Elib2EbookWeb/wwwroot/favicon.png diff --git a/Elib2EbookWeb/wwwroot/icons/icon128x128.png b/src/Elib2EbookWeb/wwwroot/icons/icon128x128.png similarity index 100% rename from Elib2EbookWeb/wwwroot/icons/icon128x128.png rename to src/Elib2EbookWeb/wwwroot/icons/icon128x128.png diff --git a/Elib2EbookWeb/wwwroot/icons/icon256x256.png b/src/Elib2EbookWeb/wwwroot/icons/icon256x256.png similarity index 100% rename from Elib2EbookWeb/wwwroot/icons/icon256x256.png rename to src/Elib2EbookWeb/wwwroot/icons/icon256x256.png diff --git a/Elib2EbookWeb/wwwroot/icons/icon32x32.png b/src/Elib2EbookWeb/wwwroot/icons/icon32x32.png similarity index 100% rename from Elib2EbookWeb/wwwroot/icons/icon32x32.png rename to src/Elib2EbookWeb/wwwroot/icons/icon32x32.png diff --git a/Elib2EbookWeb/wwwroot/icons/icon512x512.png b/src/Elib2EbookWeb/wwwroot/icons/icon512x512.png similarity index 100% rename from Elib2EbookWeb/wwwroot/icons/icon512x512.png rename to src/Elib2EbookWeb/wwwroot/icons/icon512x512.png diff --git a/Elib2EbookWeb/wwwroot/icons/icon64x64.png b/src/Elib2EbookWeb/wwwroot/icons/icon64x64.png similarity index 100% rename from Elib2EbookWeb/wwwroot/icons/icon64x64.png rename to src/Elib2EbookWeb/wwwroot/icons/icon64x64.png diff --git a/Elib2EbookWeb/wwwroot/manifest.json b/src/Elib2EbookWeb/wwwroot/manifest.json similarity index 100% rename from Elib2EbookWeb/wwwroot/manifest.json rename to src/Elib2EbookWeb/wwwroot/manifest.json diff --git a/tests/Core.Tests/Core.Tests.csproj b/tests/Core.Tests/Core.Tests.csproj new file mode 100644 index 00000000..55f07e21 --- /dev/null +++ b/tests/Core.Tests/Core.Tests.csproj @@ -0,0 +1,31 @@ + + + + false + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + ..\..\src\Core\External\EpubSharp.dll + + + + diff --git a/tests/Core.Tests/Extensions/HtmlDocumentExtensionsTests.cs b/tests/Core.Tests/Extensions/HtmlDocumentExtensionsTests.cs new file mode 100644 index 00000000..5ec5b897 --- /dev/null +++ b/tests/Core.Tests/Extensions/HtmlDocumentExtensionsTests.cs @@ -0,0 +1,79 @@ +using Core.Extensions; +using HtmlAgilityPack; + +namespace Core.Tests.Extensions; + +public class HtmlDocumentExtensionsTests +{ + private static HtmlDocument Doc(string html) + { + var d = new HtmlDocument(); + d.LoadHtml(html); + return d; + } + + [Theory] + [InlineData("

test

", "test")] + [InlineData("

hello

", "hello")] + public void AsString(string html, string expected) + { + Assert.Contains(expected, Doc(html).AsString()); + } + + [Theory] + [InlineData("

hello

", ".text", "hello")] + public void GetTextBySelector(string html, string selector, string expected) + { + Assert.Equal(expected, Doc(html).GetTextBySelector(selector)); + } + + [Theory] + [InlineData("

hello world

", "hello world")] + public void GetText(string html, string expected) + { + Assert.Contains(expected, Doc(html).DocumentNode.SelectSingleNode("//p").GetText()); + } + + [Fact] + public void GetText_NullNode() + { + Assert.Null(((HtmlNode)null).GetText()); + } + + [Theory] + [InlineData("

keep

remove", "span")] + public void RemoveNodes_WithPredicate(string html, string tagToRemove) + { + var d = Doc(html); + d.RemoveNodes(n => n.Name == tagToRemove); + Assert.Null(d.DocumentNode.SelectSingleNode($"//{tagToRemove}")); + } + + [Theory] + [InlineData("

a

b

", ".remove", "//p")] + [InlineData("
xy
", ".del", "//span")] + public void RemoveNodes_WithSelector(string html, string selector, string remainingXpath) + { + var d = Doc(html); + d.RemoveNodes(selector); + Assert.Single(d.DocumentNode.SelectNodes(remainingXpath)); + } + + [Theory] + [InlineData("
ab
", "a", 1)] + public void RemoveNodes_HtmlNodeWithPredicate(string html, string innerText, int expectedRemaining) + { + var d = Doc(html); + d.DocumentNode.SelectSingleNode("//div").RemoveNodes(n => n.InnerText == innerText); + Assert.Equal(expectedRemaining, d.DocumentNode.SelectNodes("//div/span")?.Count ?? 0); + } + + [Theory] + [InlineData("
xy
", ".del", "//div/span")] + public void RemoveNodes_HtmlNodeWithSelector(string html, string selector, string remainingXpath) + { + var d = Doc(html); + d.DocumentNode.SelectSingleNode("//div").RemoveNodes(selector); + Assert.Single(d.DocumentNode.SelectNodes(remainingXpath)); + } +} diff --git a/tests/Core.Tests/Extensions/ImageFormatExtensionsTests.cs b/tests/Core.Tests/Extensions/ImageFormatExtensionsTests.cs new file mode 100644 index 00000000..87ce811c --- /dev/null +++ b/tests/Core.Tests/Extensions/ImageFormatExtensionsTests.cs @@ -0,0 +1,24 @@ +using Core.Extensions; +using EpubSharp; +using EpubSharp.Format; + +namespace Core.Tests.Extensions; + +public class ImageFormatExtensionsTests +{ + [Theory] + [InlineData(ImageFormat.Gif, EpubContentType.ImageGif)] + [InlineData(ImageFormat.Png, EpubContentType.ImagePng)] + [InlineData(ImageFormat.Jpeg, EpubContentType.ImageJpeg)] + [InlineData(ImageFormat.Svg, EpubContentType.ImageSvg)] + public void ToEpubContentType(ImageFormat fmt, EpubContentType expected) + { + Assert.Equal(expected, fmt.ToEpubContentType()); + } + + [Fact] + public void ToEpubContentType_Unknown() + { + Assert.Throws(() => ((ImageFormat)999).ToEpubContentType()); + } +} diff --git a/tests/Core.Tests/Extensions/StreamExtensionTests.cs b/tests/Core.Tests/Extensions/StreamExtensionTests.cs new file mode 100644 index 00000000..f9bad3ae --- /dev/null +++ b/tests/Core.Tests/Extensions/StreamExtensionTests.cs @@ -0,0 +1,33 @@ +using System.Text; +using Core.Extensions; + +namespace Core.Tests.Extensions; + +public class StreamExtensionTests +{ + private static MemoryStream MakeStream(string html) + { + return new MemoryStream(Encoding.UTF8.GetBytes(html)); + } + + [Theory] + [InlineData("

test

")] + [InlineData("")] + public void AsHtmlDoc(string html) + { + Assert.NotNull(MakeStream(html).AsHtmlDoc()); + } + + [Theory] + [InlineData("

тест

", "тест")] + public void AsHtmlDoc_WithEncoding(string html, string expected) + { + Assert.Equal(expected, MakeStream(html).AsHtmlDoc(Encoding.UTF8).DocumentNode.InnerText.Trim()); + } + + [Fact] + public void AsHtmlDoc_NullEncoding() + { + Assert.NotNull(MakeStream("

hello

").AsHtmlDoc()); + } +} diff --git a/tests/Core.Tests/Extensions/StringBuilderExtensionsTests.cs b/tests/Core.Tests/Extensions/StringBuilderExtensionsTests.cs new file mode 100644 index 00000000..1019dcce --- /dev/null +++ b/tests/Core.Tests/Extensions/StringBuilderExtensionsTests.cs @@ -0,0 +1,15 @@ +using System.Text; +using Core.Extensions; + +namespace Core.Tests.Extensions; + +public class StringBuilderExtensionsTests +{ + [Theory] + [InlineData("

test

", "test")] + [InlineData("", "")] + public void AsHtmlDoc(string html, string expectedText) + { + Assert.Equal(expectedText, new StringBuilder(html).AsHtmlDoc().DocumentNode.InnerText.Trim()); + } +} diff --git a/tests/Core.Tests/Extensions/StringExtensionsTests.cs b/tests/Core.Tests/Extensions/StringExtensionsTests.cs new file mode 100644 index 00000000..88f80b90 --- /dev/null +++ b/tests/Core.Tests/Extensions/StringExtensionsTests.cs @@ -0,0 +1,105 @@ +using Core.Extensions; + +namespace Core.Tests.Extensions; + +public class StringExtensionsTests +{ + [Theory] + [InlineData("", "")] + [InlineData("Hello World.txt", "Hello World.txt")] + public void RemoveInvalidChars(string input, string expected) + { + Assert.Equal(expected, input.RemoveInvalidChars()); + } + + [Theory] + [InlineData("test", "\"test\"")] + public void CoverQuotes(string input, string expected) + { + Assert.Equal(expected, input.CoverQuotes()); + } + + [Theory] + [InlineData("hello", 10, "hello")] + [InlineData("hello world", 5, "hello")] + public void Crop(string input, int len, string expected) + { + Assert.Equal(expected, input.Crop(len)); + } + + [Theory] + [InlineData(null, null)] + [InlineData(" ", " ")] + [InlineData("text", "text")] + public void HtmlDecode(string input, string expected) + { + Assert.Equal(expected, input.HtmlDecode()); + } + + [Theory] + [InlineData("text", "b", "text")] + [InlineData("text", "", "text")] + public void CoverTag(string input, string tag, string expected) + { + Assert.Equal(expected, input.CoverTag(tag)); + } + + [Theory] + [InlineData("line1\nline2\tline3")] + [InlineData(" ")] + [InlineData(null)] + public void ReplaceNewLine(string input) + { + var result = input.ReplaceNewLine(); + if (input is null) + { + Assert.Null(result); + } + else + { + Assert.DoesNotContain("\n", result); + Assert.DoesNotContain("\t", result); + } + } + + [Theory] + [InlineData("a b c", "a b c")] + [InlineData("", "")] + public void CollapseWhitespace(string input, string expected) + { + Assert.Equal(expected, input.CollapseWhitespace()); + } + + [Fact] + public void HtmlEncode() + { + var result = "ac".HtmlEncode(); + Assert.Contains("&", result); + } + + [Fact] + public void AsUri() + { + Assert.Equal("https://example.com/", "https://example.com".AsUri().ToString()); + } + + [Fact] + public void CleanInvalidXmlChars() + { + Assert.Equal("test", "te\x00st".CleanInvalidXmlChars()); + } + + [Theory] + [InlineData("

test

")] + public void AsHtmlDoc(string html) + { + Assert.NotNull(html.AsHtmlDoc().DocumentNode); + } + + [Theory] + [InlineData("

test

")] + public void AsXHtmlDoc(string html) + { + Assert.NotNull(html.AsXHtmlDoc()); + } +} diff --git a/tests/Core.Tests/Extensions/UriExtensionTests.cs b/tests/Core.Tests/Extensions/UriExtensionTests.cs new file mode 100644 index 00000000..08c8366b --- /dev/null +++ b/tests/Core.Tests/Extensions/UriExtensionTests.cs @@ -0,0 +1,72 @@ +using Core.Extensions; + +namespace Core.Tests.Extensions; + +public class UriExtensionTests +{ + private static readonly Uri Base = new("https://example.com/path/to/file.txt?q=1&r=2"); + + [Fact] + public void GetFileName() + { + Assert.Equal("file.txt", Base.GetFileName()); + } + + [Theory] + [InlineData("https://example.com/path/to/file.txt?q=1&r=2/new", "new")] + [InlineData("https://example.com/path/to/file.txt?q=1&r=2/new/", "/new/")] + public void AppendSegment(string expected, string segment) + { + Assert.Equal(expected, Base.AppendSegment(segment).ToString()); + } + + [Fact] + public void GetSegment() + { + Assert.Equal("b", new Uri("https://example.com/a/b/c").GetSegment(2)); + } + + [Theory] + [InlineData("r=2")] + [InlineData("q=1")] + public void AppendQueryParameter(string expectedParam) + { + Assert.Contains(expectedParam, new Uri("https://example.com/page?q=1").AppendQueryParameter("r", 2).Query); + } + + [Fact] + public void GetQueryParameter() + { + Assert.Equal("value", new Uri("https://example.com/page?name=value").GetQueryParameter("name")); + } + + [Fact] + public void ReplaceHost() + { + Assert.Equal("https://new.com/path", new Uri("https://old.com/path").ReplaceHost("new.com").ToString()); + } + + [Theory] + [InlineData("https://example.com/p1", "https://example.com/p2", true)] + [InlineData("https://www.example.com/p", "https://example.com/p", true)] + [InlineData("https://example.com/p", "https://other.com/p", false)] + public void IsSameHost(string url1, string url2, bool expected) + { + Assert.Equal(expected, new Uri(url1).IsSameHost(new Uri(url2))); + } + + [Theory] + [InlineData("https://sub.example.com/p", "https://example.com/p", true)] + [InlineData("https://sub.other.com/p", "https://example.com/p", false)] + [InlineData("https://example.com/p", "https://example.com/p", true)] + public void IsSameSubDomain(string url1, string url2, bool expected) + { + Assert.Equal(expected, new Uri(url1).IsSameSubDomain(new Uri(url2))); + } + + [Fact] + public void MakeRelativeUri() + { + Assert.Equal("https://example.com/base/relative/path", new Uri("https://example.com/base/").MakeRelativeUri("relative/path").ToString()); + } +} diff --git a/tests/Core.Tests/Logic/Builders/BuilderTests.cs b/tests/Core.Tests/Logic/Builders/BuilderTests.cs new file mode 100644 index 00000000..a43e2037 --- /dev/null +++ b/tests/Core.Tests/Logic/Builders/BuilderTests.cs @@ -0,0 +1,41 @@ +using Core.Configs; +using Core.Logic.Builders; +using Microsoft.Extensions.Logging.Abstractions; + +namespace Core.Tests.Logic.Builders; + +public class BuilderTests +{ + private static Options Opt(string format) + => new( + new[] + { + "https://example.com/book" + }) + { + Format = new[] + { + format + }, + BookNamePattern = "{Book.Title}", + Timeout = 120 + }; + + public static IEnumerable BuilderData() + { + yield return ["fb2", typeof(Fb2Builder)]; + yield return ["epub", typeof(EpubBuilder)]; + yield return ["cbz", typeof(CbzBuilder)]; + yield return ["txt", typeof(TxtBuilder)]; + yield return ["json", typeof(JsonBuilder)]; + yield return ["json_lite", typeof(JsonLiteBuilder)]; + } + + [Theory] + [MemberData(nameof(BuilderData))] + public void CanCreate(string format, Type type) + { + var b = (BuilderBase)Activator.CreateInstance(type, Opt(format), NullLogger.Instance)!; + Assert.IsAssignableFrom(b); + } +} diff --git a/tests/Core.Tests/Misc/MiscTests.cs b/tests/Core.Tests/Misc/MiscTests.cs new file mode 100644 index 00000000..365fd038 --- /dev/null +++ b/tests/Core.Tests/Misc/MiscTests.cs @@ -0,0 +1,77 @@ +using Core.Configs; +using Core.Logic; +using Core.Logic.Builders; +using Core.Misc; +using Core.Types.Book; +using Microsoft.Extensions.Logging.Abstractions; + +namespace Core.Tests.Misc; + +public class BuilderProviderTests +{ + [Theory] + [InlineData("fb2", typeof(Fb2Builder))] + [InlineData("epub", typeof(EpubBuilder))] + [InlineData("json", typeof(JsonBuilder))] + [InlineData("cbz", typeof(CbzBuilder))] + [InlineData("txt", typeof(TxtBuilder))] + [InlineData("JSON_LITE", typeof(JsonLiteBuilder))] + public void Get(string format, Type type) + { + Assert.IsType( + type, + BuilderProvider.Get( + format, + new Options( + new[] + { + "https://x.com" + }), + NullLogger.Instance)); + } + + [Fact] + public void Get_Invalid() + { + Assert.Throws(() => BuilderProvider.Get( + "bad", + new Options( + new[] + { + "https://x.com" + }), + NullLogger.Instance)); + } +} + +public class BookNameBuilderLogicTests +{ + private static Book Book(string title, string author, Seria seria = null) + { + return new Book(new Uri("https://x.com")) + { + Title = title, Author = new Author(author), Seria = seria + }; + } + + [Theory] + [InlineData("{Book.Title}", "Test", "Test")] + [InlineData("{Book.Title} - {Author.Name}", "My Book", "My Book - Author")] + [InlineData("{Book.Title} - {Seria.Name} #{Seria.Number}", "Book", "Book - S #1")] + [InlineData("{Book.Title} by {Author.Name}", "Book", "Book by Author")] + public void Build(string pattern, string title, string expected) + { + var seria = pattern.Contains("Seria") ? new Seria + { + Name = "S", Number = "1" + } : null; + + Assert.Equal(expected, BookNameBuilder.Build(pattern, Book(title, "Author", seria))); + } + + [Fact] + public void Build_WithQuotes() + { + Assert.DoesNotContain("\"", BookNameBuilder.Build("\"{Book.Title}\"", Book("Test", "A"))); + } +} diff --git a/tests/Core.Tests/Misc/TempFolderTests.cs b/tests/Core.Tests/Misc/TempFolderTests.cs new file mode 100644 index 00000000..f6bc2423 --- /dev/null +++ b/tests/Core.Tests/Misc/TempFolderTests.cs @@ -0,0 +1,106 @@ +using Core.Misc.TempFolder; + +namespace Core.Tests.Misc; + +public class TempFolderTests +{ + [Fact] + public void Create_WithNullPath_CreatesInTemp() + { + using var folder = TempFolderFactory.Create(null); + Assert.NotNull(folder); + Assert.NotNull(folder.Path); + Assert.True(Directory.Exists(folder.Path)); + Assert.Contains("_e2e_temp", folder.Path); + } + + [Fact] + public void Create_WithSpecifiedPath_CreatesFolder() + { + var path = Path.Combine(Path.GetTempPath(), "test_temp_folder_" + Guid.NewGuid()); + try + { + using var folder = TempFolderFactory.Create(path); + Assert.Equal(path, folder.Path); + Assert.True(Directory.Exists(path)); + } + finally + { + if (Directory.Exists(path)) + { + Directory.Delete(path); + } + } + } + + [Fact] + public void Create_WithExistingPath_DoesNotThrow() + { + var path = Path.Combine(Path.GetTempPath(), "existing_temp_" + Guid.NewGuid()); + Directory.CreateDirectory(path); + try + { + using var folder = TempFolderFactory.Create(path); + Assert.Equal(path, folder.Path); + Assert.True(Directory.Exists(path)); + } + finally + { + if (Directory.Exists(path)) + { + Directory.Delete(path); + } + } + } + + [Fact] + public void Dispose_WithEmptyFolder_DeletesIt() + { + var path = Path.Combine(Path.GetTempPath(), "dispose_test_" + Guid.NewGuid()); + var folder = new TempFolder(path); + Directory.CreateDirectory(path); + Assert.True(Directory.Exists(path)); + folder.Dispose(); + Assert.False(Directory.Exists(path)); + } + + [Fact] + public void Dispose_WithNonEmptyFolder_DoesNotDelete() + { + var path = Path.Combine(Path.GetTempPath(), "nonempty_test_" + Guid.NewGuid()); + var folder = new TempFolder(path); + Directory.CreateDirectory(path); + File.WriteAllText(Path.Combine(path, "test.txt"), "content"); + Assert.True(Directory.Exists(path)); + folder.Dispose(); + Assert.True(Directory.Exists(path)); + Directory.Delete(path, true); + } + + [Fact] + public void Dispose_WithNonExistentFolder_DoesNotThrow() + { + var path = Path.Combine(Path.GetTempPath(), "nonexistent_" + Guid.NewGuid()); + var folder = new TempFolder(path); + var exception = Record.Exception(() => folder.Dispose()); + Assert.Null(exception); + } + + [Fact] + public void Path_IsReadonly() + { + using var folder = TempFolderFactory.Create(null); + Assert.NotNull(folder.Path); + } + + [Fact] + public void Dispose_MultipleCalls_DoesNotThrow() + { + var path = Path.Combine(Path.GetTempPath(), "multiple_dispose_" + Guid.NewGuid()); + var folder = new TempFolder(path); + Directory.CreateDirectory(path); + folder.Dispose(); + var exception = Record.Exception(() => folder.Dispose()); + Assert.Null(exception); + } +} diff --git a/tests/Core.Tests/Types/Book/BookTypeTests.cs b/tests/Core.Tests/Types/Book/BookTypeTests.cs new file mode 100644 index 00000000..c994b4a8 --- /dev/null +++ b/tests/Core.Tests/Types/Book/BookTypeTests.cs @@ -0,0 +1,165 @@ +using Core.Misc; +using Core.Types.Common; +using TypesBook = Core.Types.Book; + +namespace Core.Tests.Types.Book; + +public class BookTypeTests +{ + private static TypesBook.Book MakeBook() + => new(new Uri("https://example.com/book")); + + private static async Task CreateTempFile(string dir, string name = "cover", string ext = ".jpg") + { + return await TempFile.Create( + new Uri($"https://example.com/{name}{ext}"), + dir, + name, + ext, + new byte[] + { + 1, 2, 3 + } + ); + } + + [Fact] + public void Book_CreateWithUrl_SetsUrl() + { + var url = new Uri("https://example.com/book"); + using var book = new TypesBook.Book(url); + Assert.Equal(url, book.Url); + } + + [Theory] + [InlineData("ru")] + [InlineData("en")] + public void Book_DefaultLanguage_IsRu(string lang) + { + using var book = new TypesBook.Book(new Uri("https://example.com/book")) + { + Lang = lang + }; + + Assert.Equal(lang, book.Lang); + } + + [Fact] + public void Book_DefaultChapters_IsEmpty() + { + using var book = MakeBook(); + Assert.NotNull(book.Chapters); + Assert.Empty(book.Chapters); + } + + [Fact] + public void Book_DefaultAdditionalFiles_IsEmpty() + { + using var book = MakeBook(); + Assert.NotNull(book.AdditionalFiles); + } + + [Fact] + public void Book_DefaultCoAuthors_IsEmpty() + { + using var book = MakeBook(); + Assert.NotNull(book.CoAuthors); + Assert.Empty(book.CoAuthors); + } + + [Fact] + public async Task Book_Dispose_CoverIsNullAfterDispose() + { + var book = new TypesBook.Book(new Uri("https://example.com/book")); + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempDir); + book.Cover = await CreateTempFile(tempDir); + book.Dispose(); + Assert.False(File.Exists(book.Cover.FilePath)); + Directory.Delete(tempDir); + } + + [Fact] + public void Author_Create_SetsProperties() + { + var author = new TypesBook.Author("Test Author", new Uri("https://example.com/author")); + Assert.Equal("Test Author", author.Name); + Assert.Equal("https://example.com/author", author.Url.ToString()); + } + + [Fact] + public void Author_NullUrl_DoesNotThrow() + { + var author = new TypesBook.Author("Test Author", null); + Assert.Equal("Test Author", author.Name); + Assert.Null(author.Url); + } + + [Theory] + [InlineData("Test Seria", "1", "https://example.com/seria")] + [InlineData("Empty Number", null, null)] + public void Seria_Create_SetsProperties(string name, string number, string url) + { + var seria = new TypesBook.Seria + { + Name = name, Number = number, Url = url is null ? null : new Uri(url) + }; + + Assert.Equal(name, seria.Name); + Assert.Equal(number, seria.Number); + if (url is null) + { + Assert.Null(seria.Url); + } + else + { + Assert.NotNull(seria.Url); + } + } + + [Theory] + [InlineData("Chapter 1", "Content here")] + [InlineData("", "")] + public void Chapter_Create_SetsProperties(string title, string content) + { + var chapter = new TypesBook.Chapter + { + Title = title, Content = content + }; + + Assert.Equal(title, chapter.Title); + Assert.Equal(content, chapter.Content); + } + + [Fact] + public void AdditionalFileCollection_DefaultCollection_IsEmpty() + { + var collection = new TypesBook.AdditionalFileCollection(); + Assert.NotNull(collection); + Assert.Empty(collection.Collection); + } + + [Fact] + public async Task AdditionalFileCollection_AddFile_StoresFile() + { + var collection = new TypesBook.AdditionalFileCollection(); + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempDir); + + var tempFile = await CreateTempFile(tempDir, "file", ".jpg"); + + collection.Add(AdditionalTypeEnum.Images, tempFile); + var files = collection.Get(AdditionalTypeEnum.Images); + Assert.Single(files); + Assert.Same(tempFile, files[0]); + Directory.Delete(tempDir, true); + } + + [Fact] + public void AdditionalFileCollection_GetNonExistentType_ReturnsEmpty() + { + var collection = new TypesBook.AdditionalFileCollection(); + var files = collection.Get(AdditionalTypeEnum.Images); + Assert.Empty(files); + } +} From 3e5e0eea8d980d6d46f2a218962572737126ecbe Mon Sep 17 00:00:00 2001 From: OnlyFart Date: Tue, 7 Jul 2026 23:18:27 +0300 Subject: [PATCH 04/29] Update dotnet.yml --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index aca24f56..974674a9 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -102,7 +102,7 @@ jobs: - uses: kzrnm/get-net-sdk-project-versions-action@v2 id: get-version with: - proj-path: src/Core/Core.csproj + proj-path: Directory.Build.props - name: "get-last-release" id: last_release From e213dc76c62ba53875b4026fafee536c24937c9d Mon Sep 17 00:00:00 2001 From: OnlyFart Date: Tue, 7 Jul 2026 23:25:35 +0300 Subject: [PATCH 05/29] Update GitHub Actions to Node.js 24 compatible versions (#180) --- .github/workflows/dotnet.yml | 51 +++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 974674a9..af2c89d8 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -19,7 +19,7 @@ jobs: - name: Restore dependencies run: dotnet restore Elib2Ebook.sln - + - name: Build run: dotnet build --no-restore -c release Elib2Ebook.sln @@ -93,28 +93,37 @@ jobs: if: github.ref == 'refs/heads/master' outputs: VERSION: ${{ steps.get-version.outputs.version }} - LAST_TAG: ${{ steps.last_release.outputs.tag_name }} + LAST_TAG: ${{ steps.last_release_tag.outputs.tag_name }} steps: - name: check repository uses: actions/checkout@v5 - - uses: kzrnm/get-net-sdk-project-versions-action@v2 + - uses: kzrnm/get-net-sdk-project-versions-action@v3.1.0 id: get-version with: proj-path: Directory.Build.props - name: "get-last-release" id: last_release - uses: InsonusK/get-latest-release@v1.1.0 + uses: octokit/request-action@v3.0.0 with: - myToken: ${{ github.token }} + route: GET /repos/{owner}/{repo}/releases/latest + owner: OnlyFart + repo: Elib2Ebook + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: extract-last-release-tag + id: last_release_tag + run: echo "tag_name=$(echo '${{ steps.last_release.outputs.data }}' | jq -r '.tag_name')" >> $GITHUB_OUTPUT push_to_registry: name: push docker image to hub runs-on: ubuntu-latest needs: check_version if: ${{ needs.check_version.outputs.LAST_TAG != needs.check_version.outputs.VERSION }} + strategy: matrix: folder: [ Elib2EbookCli, Elib2EbookWeb ] @@ -153,19 +162,25 @@ jobs: runs-on: ubuntu-latest if: ${{ needs.check_version.outputs.LAST_TAG != needs.check_version.outputs.VERSION }} outputs: - UPLOAD_URL: ${{ steps.create_release.outputs.upload_url }} + TAG_NAME: ${{ steps.tag_name.outputs.tag }} steps: - - name: release - uses: actions/create-release@v1 + - name: check repository + uses: actions/checkout@v5 + + - name: create release + uses: softprops/action-gh-release@v2 id: create_release with: draft: false prerelease: false - release_name: ${{needs.check_version.outputs.VERSION}} + name: ${{needs.check_version.outputs.VERSION}} tag_name: ${{needs.check_version.outputs.VERSION}} - env: - GITHUB_TOKEN: ${{ github.token }} + token: ${{ github.token }} + + - name: save tag name + id: tag_name + run: echo "tag=${{needs.check_version.outputs.VERSION}}" >> $GITHUB_OUTPUT upload_release: needs: [create_release, publish] @@ -182,19 +197,13 @@ jobs: name: Elib2Ebook-${{ matrix.os }}-${{ matrix.version }} path: ${{ github.workspace }}/${{ matrix.os }}-${{ matrix.version }} - - name: Install zip - uses: montudor/action-zip@v0.1.0 - - name: Zip output run: zip -qq -x "*.pdb" -r ../Elib2Ebook-${{ matrix.os }}-${{ matrix.version }}.zip . working-directory: ${{ github.workspace }}/${{ matrix.os }}-${{ matrix.version }} - name: upload ${{ matrix.os }}-${{ matrix.version }} asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ github.token }} + uses: softprops/action-gh-release@v2 with: - upload_url: ${{ needs.create_release.outputs.UPLOAD_URL }} - asset_path: Elib2Ebook-${{ matrix.os }}-${{ matrix.version }}.zip - asset_name: Elib2Ebook-${{ matrix.os }}-${{ matrix.version }}.zip - asset_content_type: application/zip + tag_name: ${{ needs.create_release.outputs.TAG_NAME }} + files: Elib2Ebook-${{ matrix.os }}-${{ matrix.version }}.zip + token: ${{ github.token }} From 5edfaffecb8a108b3d09db34431c5dc8005c8c33 Mon Sep 17 00:00:00 2001 From: OnlyFart Date: Wed, 8 Jul 2026 12:08:00 +0300 Subject: [PATCH 06/29] chore: move types from Core to ExternalServices projects (#181) --- ARCHITECTURE.md | 235 +++++ Directory.Packages.props | 2 + Elib2Ebook.sln | 995 +++++++++++++++++- README.md | 15 +- src/Core/Logic/Getters/RanobeNovelsGetter.cs | 112 -- src/Core/Misc/GetterProvider.cs | 17 - .../AuthorToday/AuthorTodayBookDetails.cs | 32 - .../Types/AuthorToday/AuthorTodayImage.cs | 8 - src/Core/Types/AuthorToday/AuthorTodayUser.cs | 8 - .../BookYandex/BooksYandexAudioBitrate.cs | 8 - .../BookYandex/BooksYandexAudioOffline.cs | 10 - .../Types/BookYandex/BooksYandexAudioTrack.cs | 8 - .../Types/BookYandex/BooksYandexAuthor.cs | 10 - src/Core/Types/BookYandex/BooksYandexBook.cs | 44 - .../BookYandex/BooksYandexComicContent.cs | 8 - .../BookYandex/BooksYandexComicMetadata.cs | 8 - .../Types/BookYandex/BooksYandexComicPage.cs | 8 - src/Core/Types/BookYandex/BooksYandexCover.cs | 10 - .../Types/BookYandex/BooksYandexEpisode.cs | 12 - .../Types/BookYandex/BooksYandexEpisodes.cs | 8 - .../Types/BookYandex/BooksYandexPlaylist.cs | 8 - .../Types/BookYandex/BooksYandexResponse.cs | 12 - src/Core/Types/BookYandex/BooksYandexUri.cs | 10 - .../Types/Bookriver/BookRiverApiResponse.cs | 8 - .../Types/Bookriver/BookRiverAuthResponse.cs | 8 - src/Core/Types/Bookriver/BookRiverBook.cs | 8 - src/Core/Types/Bookriver/BookRiverChapter.cs | 10 - .../Bookriver/BookRiverChapterContent.cs | 8 - .../Types/Bookriver/BookRiverCurrentBook.cs | 8 - .../Types/Bookstab/BookstabApiResponse.cs | 10 - src/Core/Types/Bookstab/BookstabBook.cs | 16 - src/Core/Types/Bookstab/BookstabChapter.cs | 12 - src/Core/Types/Bookstab/BookstabUser.cs | 10 - .../Types/Bookstime/BookstimeAuthResponse.cs | 8 - src/Core/Types/Boosty/BoostyApiResponse.cs | 10 - src/Core/Types/Boosty/BoostyExtra.cs | 10 - src/Core/Types/Boosty/BoostyPost.cs | 12 - .../Types/DarkNovels/DarkNovelsAuthData.cs | 8 - .../Types/DarkNovels/DarkNovelsAuthToken.cs | 8 - .../Types/DarkNovels/DarkNovelsChapter.cs | 12 - src/Core/Types/DarkNovels/DarkNovelsData.cs | 12 - src/Core/Types/Dreame/DreameApiResponse.cs | 8 - src/Core/Types/Dreame/DreameCatalog.cs | 8 - src/Core/Types/Dreame/DreameChapter.cs | 12 - src/Core/Types/Dreame/DreameNovel.cs | 20 - src/Core/Types/Dreame/DreamePager.cs | 8 - .../Types/FanFicus/FanFicusApiResponse.cs | 8 - src/Core/Types/FanFicus/FanFicusBook.cs | 14 - src/Core/Types/FanFicus/FanFicusChapter.cs | 8 - src/Core/Types/FanFicus/FanFicusCreator.cs | 10 - src/Core/Types/FanFicus/FanFicusImage.cs | 8 - src/Core/Types/FanFicus/FanFicusPart.cs | 10 - src/Core/Types/FanFicus/FanFicusUser.cs | 8 - .../Types/Freedlit/FreedlitApiResponse.cs | 8 - src/Core/Types/Freedlit/FreedlitApp.cs | 8 - src/Core/Types/Freedlit/FreedlitAuthError.cs | 10 - src/Core/Types/Freedlit/FreedlitAuthor.cs | 12 - src/Core/Types/Freedlit/FreedlitBook.cs | 18 - src/Core/Types/Freedlit/FreedlitChapter.cs | 12 - .../Types/Freedlit/FreedlitItemsContent.cs | 8 - src/Core/Types/Freedlit/FreedlitProps.cs | 10 - .../Types/Freedlit/FreedlitSuccessResponse.cs | 8 - .../HotNovelPub/HotNovelPubApiResponse.cs | 8 - .../Types/HotNovelPub/HotNovelPubAuthorize.cs | 12 - src/Core/Types/HotNovelPub/HotNovelPubBook.cs | 14 - .../HotNovelPub/HotNovelPubBookResponse.cs | 10 - .../Types/HotNovelPub/HotNovelPubChapter.cs | 12 - .../Types/Librebook/LibrebookAuthResponse.cs | 10 - src/Core/Types/Litexit/LitexitChapter.cs | 12 - src/Core/Types/Litexit/LitexitUser.cs | 8 - .../Types/Litgorod/LitgorodAuthResponse.cs | 8 - src/Core/Types/Litgorod/LitgorodBookPart.cs | 8 - src/Core/Types/Litgorod/LitgorodChapter.cs | 8 - src/Core/Types/Litmarket/AuthResponse.cs | 8 - src/Core/Types/Litmarket/Block.cs | 10 - src/Core/Types/Litmarket/Chunk.cs | 8 - src/Core/Types/Litmarket/LBook.cs | 8 - src/Core/Types/Litmarket/Mod.cs | 17 - src/Core/Types/Litmarket/Response.cs | 10 - src/Core/Types/Litnet/LitnetAuthResponse.cs | 10 - src/Core/Types/Litnet/LitnetBookResponse.cs | 28 - .../Types/Litnet/LitnetChapterResponse.cs | 10 - .../Types/Litnet/LitnetContentsResponse.cs | 10 - .../Types/Litres/Requests/LitresAuthData.cs | 10 - .../Litres/Requests/LitresBrowseArtsData.cs | 12 - .../Litres/Requests/LitresRequestBase.cs | 12 - src/Core/Types/Litres/Response/LitresArt.cs | 22 - .../Litres/Response/LitresArtTypeEnum.cs | 8 - .../Litres/Response/LitresAuthResponseData.cs | 12 - src/Core/Types/Litres/Response/LitresFile.cs | 10 - src/Core/Types/Litres/Response/LitresFiles.cs | 8 - src/Core/Types/Litres/Response/LitresMe.cs | 8 - .../Response/LitresPartnerSubscriptions.cs | 8 - .../Types/Litres/Response/LitresPerson.cs | 14 - .../Types/Litres/Response/LitresResponse.cs | 8 - src/Core/Types/Litres/Response/LitresSeria.cs | 12 - .../Litres/Response/LitresStaticResponse.cs | 8 - .../Litres/Response/LitresSubscription.cs | 12 - .../Types/Litsovet/LitsovetLoginResponse.cs | 8 - src/Core/Types/MangaLib/MangaLibPg.cs | 10 - src/Core/Types/MyBook/MyBookAuth.cs | 10 - src/Core/Types/MyBook/MyBookAuthor.cs | 10 - src/Core/Types/MyBook/MyBookBook.cs | 27 - src/Core/Types/MyBook/MyBookFile.cs | 12 - src/Core/Types/MyBook/MyBookMapFile.cs | 8 - src/Core/Types/Neobook/NeobookAttachment.cs | 8 - src/Core/Types/Neobook/NeobookAuth.cs | 10 - src/Core/Types/Neobook/NeobookBook.cs | 10 - src/Core/Types/Neobook/NeobookChapter.cs | 8 - src/Core/Types/Neobook/NeobookChapterData.cs | 8 - src/Core/Types/Neobook/NeobookLogin.cs | 10 - src/Core/Types/Neobook/NeobookPostData.cs | 16 - src/Core/Types/Neobook/NeobookTocChapter.cs | 10 - src/Core/Types/Neobook/NeobookUser.cs | 12 - src/Core/Types/Ranobe/RanobeBook.cs | 16 - src/Core/Types/Ranobe/RanobeChapter.cs | 8 - src/Core/Types/Ranobe/RanobeChapterContent.cs | 8 - src/Core/Types/Ranobe/RanobeChapterShort.cs | 10 - src/Core/Types/Ranobe/RanobeImage.cs | 10 - .../Types/RanobeHub/RanobeHubApiResponse.cs | 8 - src/Core/Types/RanobeHub/RanobeHubChapter.cs | 10 - src/Core/Types/RanobeHub/RanobeHubVolume.cs | 8 - .../Types/RanobeNovels/RanobeNovelsChapter.cs | 10 - src/Core/Types/RanobeOvh/RanobeOvhBranch.cs | 12 - .../Types/RanobeOvh/RanobeOvhChapterFull.cs | 56 - src/Core/Types/RanobeOvh/RanobeOvhManga.cs | 12 - .../Types/RanobeOvh/RanobeOvhMangaName.cs | 8 - src/Core/Types/RanobeOvh/RanobeOvhPage.cs | 10 - .../Types/RanobeOvh/RanoveOvhTranslator.cs | 10 - src/Core/Types/Ranobes/RanobesCookie.cs | 8 - .../Types/Renovels/RenovelsApiResponse.cs | 10 - .../Types/Renovels/RenovelsAuthResponse.cs | 8 - src/Core/Types/Renovels/RenovelsBranch.cs | 10 - src/Core/Types/Renovels/RenovelsChapter.cs | 37 - src/Core/Types/Renovels/RenovelsContent.cs | 18 - src/Core/Types/Renovels/RenovelsPage.cs | 8 - src/Core/Types/Renovels/RenovelsPublisher.cs | 10 - .../Types/Renovels/RenovelsTocResponse.cs | 8 - src/Core/Types/Rulate/RulateAuthResponse.cs | 8 - src/Core/Types/Rulate/RulateChapter.cs | 10 - src/Core/Types/SocialLib/LibSocialToken.cs | 8 - .../Types/SocialLib/RanobeLibBookDetail.cs | 42 - .../Types/SocialLib/SocialLibBookChapters.cs | 82 -- src/Core/Types/SocialLib/SocialLibChapter.cs | 18 - .../Types/SocialLib/SocialLibChapterOld.cs | 19 - src/Core/Types/SocialLib/SocialLibChapters.cs | 8 - src/Core/Types/SocialLib/User.cs | 8 - src/Core/Types/SocialLib/WindowData.cs | 10 - .../StrokiMts/StrokiMtsApiMultiResponse.cs | 8 - .../Types/StrokiMts/StrokiMtsApiResponse.cs | 8 - src/Core/Types/StrokiMts/StrokiMtsAuthor.cs | 10 - src/Core/Types/StrokiMts/StrokiMtsBookItem.cs | 18 - .../Types/StrokiMts/StrokiMtsContentInfo.cs | 8 - src/Core/Types/StrokiMts/StrokiMtsFile.cs | 8 - src/Core/Types/StrokiMts/StrokiMtsFileUrl.cs | 8 - src/Core/Types/StrokiMts/StrokiMtsFiles.cs | 10 - .../Types/StrokiMts/StrokiMtsMultiItem.cs | 14 - src/Core/Types/Wattpad/WattpadInfo.cs | 16 - src/Core/Types/Wattpad/WattpadMeta.cs | 6 - src/Core/Types/Wattpad/WattpadPart.cs | 15 - src/Core/Types/Wattpad/WattpadUser.cs | 8 - src/Core/Types/WuxiaWorld/WuxiaWorldToc.cs | 8 - .../Book/AdditionalFileCollection.cs | 5 +- .../Book}/AdditionalTypeEnum.cs | 2 +- .../Book/Author.cs | 2 +- .../Types => Elib2Ebook.Domain}/Book/Book.cs | 4 +- .../Book/Chapter.cs | 4 +- .../Types => Elib2Ebook.Domain}/Book/Seria.cs | 2 +- .../Common/IdChapter.cs | 2 +- .../Common/TempFile.cs | 2 +- .../Common/UrlChapter.cs | 2 +- .../Elib2Ebook.Domain.csproj | 3 + .../BookNameBuilder.cs | 6 +- .../Builders/AdditionaFileBuilder.cs | 9 +- .../Builders/BuilderBase.cs | 10 +- .../Builders/CbzBuilder.cs | 6 +- .../Builders/EpubBuilder.cs | 10 +- .../Builders/Fb2Builder.cs | 10 +- .../Builders/JsonBuilder.cs | 6 +- .../Builders/JsonLiteBuilder.cs | 8 +- .../Builders/TxtBuilder.cs | 8 +- .../Configs/BookGetterConfig.cs | 6 +- .../Configs/Options.cs | 31 +- .../Elib2Ebook.DomainServices.csproj} | 25 +- .../Extensions/FileProviderExtensions.cs | 2 +- .../Extensions/HtmlDocumentExtensions.cs | 2 +- .../Extensions/HttpClientExtensions.cs | 2 +- .../Extensions/ImageFormatExtensions.cs | 2 +- .../Extensions/StreamExtension.cs | 2 +- .../Extensions/StringBuilderExtensions.cs | 2 +- .../Extensions/StringExtensions.cs | 2 +- .../Extensions/UriExtension.cs | 2 +- .../External/EpubSharp.deps.json | 0 .../External/EpubSharp.dll | Bin .../External/EpubSharp.pdb | Bin .../FileProvider.cs | 2 +- .../Getters/GetterBase.cs | 10 +- .../Misc/BuilderProvider.cs | 6 +- .../Misc/GetterFactory.cs | 60 ++ .../Misc/TempFolder/TempFolder.cs | 2 +- .../Misc/TempFolder/TempFolderFactory.cs | 2 +- .../Patterns/ChapterPattern.xhtml | 0 .../Patterns/Roboto-Italic.ttf | Bin .../Patterns/Roboto-Regular.ttf | Bin .../Patterns/stylesheet.css | 0 src/Elib2EbookCli/Elib2EbookCli.csproj | 2 +- src/Elib2EbookCli/Program.cs | 10 +- src/Elib2EbookWeb/Components/BookForm.razor | 7 +- .../Components/Pages/Error.razor | 3 +- src/Elib2EbookWeb/Components/Pages/Home.razor | 12 +- src/Elib2EbookWeb/Components/ShortBook.razor | 6 +- src/Elib2EbookWeb/Elib2EbookWeb.csproj | 2 +- .../AcomicsGetter.cs | 9 +- ...Elib2Ebook.ExternalServices.Acomics.csproj | 5 + .../AuthorTodayGetter.cs | 36 +- ...2Ebook.ExternalServices.AuthorToday.csproj | 5 + .../Types/AuthResponse.cs} | 4 +- .../Types}/AuthorTodayChapter.cs | 28 +- .../Types/BookDetails.cs | 45 + .../Types/Image.cs | 9 + .../Types/User.cs | 9 + .../BookInBookGetter.cs | 11 +- ...b2Ebook.ExternalServices.BookInBook.csproj | 5 + .../BookRiverGetter.cs | 19 +- ...ib2Ebook.ExternalServices.BookRiver.csproj | 5 + .../Types/ApiResponse.cs | 9 + .../Types/AuthResponse.cs | 9 + .../Types/BookRiverBook.cs | 9 + .../Types/BookRiverChapter.cs | 12 + .../Types/BookRiverCurrentBook.cs | 9 + .../Types/ChapterContent.cs | 9 + .../BookinistGetter.cs | 11 +- ...ib2Ebook.ExternalServices.Bookinist.csproj | 5 + ...2Ebook.ExternalServices.BooksYandex.csproj | 5 + .../Getters}/BooksYandexBooksGetter.cs | 13 +- .../Getters}/BooksYandexComicsGetter.cs | 10 +- .../Getters}/BooksYandexGetterBase.cs | 15 +- .../Types/BooksYandexAudioBitrate.cs | 9 + .../Types/BooksYandexAudioOffline.cs | 12 + .../Types/BooksYandexAudioTrack.cs | 9 + .../Types/BooksYandexAuthor.cs | 12 + .../Types/BooksYandexBook.cs | 54 + .../Types/BooksYandexComicContent.cs | 9 + .../Types/BooksYandexComicMetadata.cs | 9 + .../Types/BooksYandexComicPage.cs | 9 + .../Types/BooksYandexCover.cs | 12 + .../Types/BooksYandexEpisode.cs | 15 + .../Types/BooksYandexEpisodes.cs | 9 + .../Types/BooksYandexPlaylist.cs | 9 + .../Types/BooksYandexResponse.cs | 15 + .../Types/BooksYandexUri.cs | 12 + .../BookstabGetter.cs | 23 +- ...lib2Ebook.ExternalServices.Bookstab.csproj | 5 + .../Types/ApiResponse.cs | 12 + .../Types/BookstabBook.cs | 21 + .../Types/BookstabChapter.cs | 15 + .../Types/User.cs | 12 + .../BookstimeGetter.cs | 15 +- ...ib2Ebook.ExternalServices.Bookstime.csproj | 5 + .../Types/AuthResponse.cs | 9 + .../BookurukGetter.cs | 11 +- ...lib2Ebook.ExternalServices.Bookuruk.csproj | 5 + .../BoostyGetter.cs | 13 +- .../Elib2Ebook.ExternalServices.Boosty.csproj | 5 + .../Types/BoostyApiResponse.cs | 12 + .../Types/BoostyExtra.cs | 12 + .../Types/BoostyPost.cs | 15 + .../BoovelGetter.cs | 11 +- .../Elib2Ebook.ExternalServices.Boovel.csproj | 5 + .../DarkNovelsGetter.cs | 13 +- ...b2Ebook.ExternalServices.DarkNovels.csproj | 5 + .../Types/DarkNovelsAuthData.cs | 9 + .../Types/DarkNovelsAuthToken.cs | 9 + .../Types/DarkNovelsChapter.cs | 15 + .../Types/DarkNovelsData.cs | 15 + .../DesuGetter.cs | 11 +- .../Elib2Ebook.ExternalServices.Desu.csproj | 5 + .../DreameGetter.cs | 13 +- .../Elib2Ebook.ExternalServices.Dreame.csproj | 5 + .../Types/DreameApiResponse.cs | 9 + .../Types/DreameCatalog.cs | 9 + .../Types/DreameChapter.cs | 15 + .../Types/DreameNovel.cs | 27 + .../Types/DreamePager.cs | 9 + ...lib2Ebook.ExternalServices.FanFicus.csproj | 5 + .../FanFicusGetter.cs | 13 +- .../Types/FanFicusApiResponse.cs | 9 + .../Types/FanFicusBook.cs | 18 + .../Types/FanFicusChapter.cs | 9 + .../Types/FanFicusCreator.cs | 12 + .../Types/FanFicusImage.cs | 9 + .../Types/FanFicusPart.cs | 12 + .../Types/FanFicusUser.cs | 9 + .../Elib2Ebook.ExternalServices.Fb2Top.csproj | 5 + .../Getters}/Fb2TopGetter.cs | 4 +- .../Getters}/Fb2TopGetterBase.cs | 11 +- .../Getters}/LadyLibGetter.cs | 4 +- ...Elib2Ebook.ExternalServices.Ficbook.csproj | 5 + .../FicbookGetter.cs | 11 +- ...2Ebook.ExternalServices.FictionBook.csproj | 5 + .../FictionBookGetter.cs | 11 +- ...lib2Ebook.ExternalServices.Freedlit.csproj | 5 + .../FreedlitGetter.cs | 13 +- .../Types/FreedlitApiResponse.cs | 9 + .../Types/FreedlitApp.cs | 9 + .../Types/FreedlitAuthError.cs | 12 + .../Types/FreedlitAuthor.cs | 15 + .../Types/FreedlitBook.cs | 24 + .../Types/FreedlitChapter.cs | 15 + .../Types/FreedlitItemsContent.cs | 9 + .../Types/FreedlitProps.cs | 12 + .../Types/FreedlitSuccessResponse.cs | 9 + ...Elib2Ebook.ExternalServices.Freedom.csproj | 5 + .../Getters}/BookHamsterGetter.cs | 4 +- .../Getters}/FreedomGetter.cs | 4 +- .../Getters}/FreedomGetterBase.cs | 11 +- ...2Ebook.ExternalServices.HogwartsNet.csproj | 5 + .../HogwartsNetGetter.cs | 11 +- ...2Ebook.ExternalServices.HotNovelPub.csproj | 5 + .../Getters}/EzNovelsGetter.cs | 4 +- .../Getters}/HotNovelPubGetter.cs | 4 +- .../Getters}/HotNovelPubGetterBase.cs | 13 +- .../Getters}/LaNovelsGetter.cs | 4 +- .../Getters}/LightNovelDailyGetter.cs | 4 +- .../Types/HotNovelPubApiResponse.cs | 9 + .../Types/HotNovelPubAuthorize.cs | 15 + .../Types/HotNovelPubBook.cs | 18 + .../Types/HotNovelPubBookResponse.cs | 12 + .../Types/HotNovelPubChapter.cs | 15 + ...Elib2Ebook.ExternalServices.HubBook.csproj | 5 + .../HubBookGetter.cs | 11 +- .../Elib2Ebook.ExternalServices.IGram.csproj | 5 + .../IGramGetter.cs | 13 +- .../Types/LitexitChapter.cs | 15 + .../Types/LitexitUser.cs | 9 + .../Elib2Ebook.ExternalServices.Jaomix.csproj | 5 + .../JaomixGetter.cs | 11 +- ...ib2Ebook.ExternalServices.LibSocial.csproj | 5 + .../Getters}/HentaiLibGetter.cs | 4 +- .../Getters}/MangaLibGetter.cs | 4 +- .../Getters}/MangaLibGetterBase.cs | 8 +- .../Getters}/MangaLibOrgGetter.cs | 4 +- .../Getters}/NewLibSocialGetterBase.cs | 13 +- .../Getters}/RanobeLibGetter.cs | 8 +- .../Getters}/SlashLibGetter.cs | 4 +- .../Types/MangaLib/MangaLibPg.cs | 12 + .../Types/SocialLib/LibSocialToken.cs | 9 + .../Types/SocialLib/RanobeLibBookDetail.cs | 53 + .../Types/SocialLib/SocialLibBookChapters.cs | 100 ++ .../Types/SocialLib/SocialLibChapter.cs | 23 + .../Types/SocialLib/SocialLibChapterOld.cs | 23 + .../Types/SocialLib/SocialLibChapters.cs | 9 + .../Types/SocialLib/User.cs | 9 + .../Types/SocialLib/WindowData.cs | 12 + .../Elib2Ebook.ExternalServices.Libbox.csproj | 5 + .../LibboxGetter.cs | 11 +- ...Elib2Ebook.ExternalServices.Libking.csproj | 5 + .../LibkingGetter.cs | 11 +- ...ib2Ebook.ExternalServices.Librebook.csproj | 5 + .../LibrebookGetter.cs | 13 +- .../Types/LibrebookAuthResponse.cs | 12 + .../Elib2Ebook.ExternalServices.Libst.csproj | 5 + .../LibstGetter.cs | 11 +- ...Elib2Ebook.ExternalServices.LitLife.csproj | 5 + .../LitLifeGetter.cs | 11 +- ...lib2Ebook.ExternalServices.Litgorod.csproj | 5 + .../LitgorodGetter.cs | 13 +- .../Types/LitgorodAuthResponse.cs | 9 + .../Types/LitgorodBookPart.cs | 9 + .../Types/LitgorodChapter.cs | 9 + ...ib2Ebook.ExternalServices.Litmarket.csproj | 5 + .../LitmarketGetter.cs | 13 +- .../Types/AuthResponse.cs | 9 + .../Types/Block.cs | 12 + .../Types/Chunk.cs | 9 + .../Types/LBook.cs | 9 + .../Types/Mod.cs | 22 + .../Types/Response.cs | 12 + .../Elib2Ebook.ExternalServices.Litmir.csproj | 5 + .../LitmirGetter.cs | 11 +- .../Elib2Ebook.ExternalServices.Litnet.csproj | 5 + .../Getters}/BooknetComGetter.cs | 4 +- .../Getters}/BooknetUaGetter.cs | 4 +- .../Getters}/LitnetGetter.cs | 4 +- .../Getters}/LitnetGetterBase.cs | 13 +- .../Types/LitnetAuthResponse.cs | 12 + .../Types/LitnetBookResponse.cs | 39 + .../Types/LitnetChapterResponse.cs | 12 + .../Types/LitnetContentsResponse.cs | 12 + .../Elib2Ebook.ExternalServices.Litres.csproj | 5 + .../LitresGetter.cs | 18 +- .../Types}/LitresBook.cs | 4 +- .../Types}/LitresPayload.cs | 22 +- .../Types}/LitresTarget.cs | 4 +- .../Types/Requests/LitresAuthData.cs | 12 + .../Types}/Requests/LitresAuthRequest.cs | 4 +- .../Types/Requests/LitresBrowseArtsData.cs | 15 + .../Requests/LitresBrowseArtsRequest.cs | 4 +- .../Types/Requests/LitresRequestBase.cs | 15 + .../Types/Response/LitresArt.cs | 30 + .../Types/Response/LitresArtTypeEnum.cs | 8 + .../Types/Response/LitresAuthResponseData.cs | 15 + .../Types/Response/LitresFile.cs | 12 + .../Types/Response/LitresFiles.cs | 9 + .../Types/Response/LitresMe.cs | 9 + .../Response/LitresPartnerSubscriptions.cs | 9 + .../Types/Response/LitresPerson.cs | 18 + .../Types/Response/LitresResponse.cs | 9 + .../Types/Response/LitresSeria.cs | 15 + .../Types/Response/LitresStaticResponse.cs | 9 + .../Types/Response/LitresSubscription.cs | 15 + ...lib2Ebook.ExternalServices.Litsovet.csproj | 5 + .../LitsovetGetter.cs | 13 +- .../Types/LitsovetLoginResponse.cs | 9 + ...b2Ebook.ExternalServices.MangaMammy.csproj | 5 + .../MangaMammyGetter.cs | 11 +- ...Elib2Ebook.ExternalServices.MirKnig.csproj | 5 + .../MirKnigGetter.cs | 11 +- .../Elib2Ebook.ExternalServices.MyBook.csproj | 8 + .../MyBookGetter.cs | 14 +- .../Types/MyBookAuth.cs | 12 + .../Types/MyBookAuthor.cs | 12 + .../Types/MyBookBook.cs | 37 + .../Types/MyBookFile.cs | 15 + .../Types/MyBookMapFile.cs | 9 + ...Elib2Ebook.ExternalServices.Neobook.csproj | 5 + .../NeobookGetter.cs | 13 +- .../Types/NeobookAttachment.cs | 9 + .../Types/NeobookAuth.cs | 12 + .../Types/NeobookBook.cs | 12 + .../Types/NeobookChapter.cs | 9 + .../Types/NeobookChapterData.cs | 9 + .../Types/NeobookLogin.cs | 12 + .../Types/NeobookPostData.cs | 21 + .../Types/NeobookTocChapter.cs | 12 + .../Types/NeobookUser.cs | 15 + ...ook.ExternalServices.NovelTranslate.csproj | 5 + .../NovelTranslateGetter.cs | 11 +- ...ib2Ebook.ExternalServices.Novelhall.csproj | 5 + .../NovelhallGetter.cs | 11 +- ...Elib2Ebook.ExternalServices.Novelxo.csproj | 5 + .../Getters}/NovelxoGetter.cs | 4 +- .../Getters}/NovelxoGetterBase.cs | 11 +- .../Getters}/NovelxoRuGetter.cs | 4 +- ...2Ebook.ExternalServices.OnlineKnigi.csproj | 5 + .../OnlineKnigiGetter.cs | 11 +- ...lib2Ebook.ExternalServices.Prodaman.csproj | 5 + .../ProdamanGetter.cs | 11 +- .../Elib2Ebook.ExternalServices.Ranobe.csproj | 5 + .../RanobeGetter.cs | 13 +- .../Types/RanobeBook.cs | 21 + .../Types/RanobeChapter.cs | 9 + .../Types/RanobeChapterContent.cs | 9 + .../Types/RanobeChapterShort.cs | 12 + .../Types/RanobeImage.cs | 12 + ...ib2Ebook.ExternalServices.RanobeHub.csproj | 5 + .../RanobeHubGetter.cs | 13 +- .../Types/RanobeHubApiResponse.cs | 9 + .../Types/RanobeHubChapter.cs | 12 + .../Types/RanobeHubVolume.cs | 9 + ...ib2Ebook.ExternalServices.RanobeOvh.csproj | 5 + .../Getters}/MangaOvhGetter.cs | 8 +- .../Getters}/NovelOvhGetter.cs | 8 +- .../Getters}/RanobeOvhGetterBase.cs | 13 +- .../Types/RanobeOvhBranch.cs | 15 + .../Types/RanobeOvhChapterFull.cs | 69 ++ .../Types/RanobeOvhManga.cs | 15 + .../Types/RanobeOvhMangaName.cs | 9 + .../Types/RanobeOvhPage.cs | 12 + .../Types/RanoveOvhTranslator.cs | 12 + ...b2Ebook.ExternalServices.RanobesCom.csproj | 5 + .../RanobesComGetter.cs | 13 +- .../Types/RanobesCookie.cs | 9 + ...ib2Ebook.ExternalServices.ReadManga.csproj | 5 + .../ReadMangaGetter.cs | 11 +- .../Elib2Ebook.ExternalServices.Readli.csproj | 5 + .../ReadliGetter.cs | 11 +- ...lib2Ebook.ExternalServices.Renovels.csproj | 5 + .../Getters}/RemangaGetter.cs | 8 +- .../Getters}/RenovelsGetter.cs | 8 +- .../Getters}/RenovelsGetterBase.cs | 13 +- .../Types/RenovelsApiResponse.cs | 12 + .../Types/RenovelsAuthResponse.cs | 9 + .../Types/RenovelsBranch.cs | 12 + .../Types/RenovelsChapter.cs | 45 + .../Types/RenovelsContent.cs | 24 + .../Types/RenovelsPage.cs | 9 + .../Types/RenovelsPublisher.cs | 12 + .../Types/RenovelsTocResponse.cs | 9 + ...Elib2Ebook.ExternalServices.Romfant.csproj | 5 + .../RomfantGetter.cs | 11 +- ...ib2Ebook.ExternalServices.RoyalRoad.csproj | 5 + .../RoyalRoadGetter.cs | 11 +- .../Elib2Ebook.ExternalServices.Rulate.csproj | 5 + .../Getters}/BllateGetter.cs | 4 +- .../Getters}/ErolateGetter.cs | 4 +- .../Getters}/MlateGetter.cs | 4 +- .../Getters}/RulateGetter.cs | 4 +- .../Getters}/RulateGetterBase.cs | 13 +- .../Types/RulateAuthResponse.cs | 9 + .../Types/RulateChapter.cs | 12 + .../Elib2Ebook.ExternalServices.Samlib.csproj | 5 + .../SamlibGetter.cs | 11 +- ...ib2Ebook.ExternalServices.StrokiMts.csproj | 5 + .../StrokiMtsGetter.cs | 14 +- .../Types/StrokiMtsApiMultiResponse.cs | 9 + .../Types/StrokiMtsApiResponse.cs | 9 + .../Types/StrokiMtsAuthor.cs | 12 + .../Types/StrokiMtsBookItem.cs | 24 + .../Types/StrokiMtsContentInfo.cs | 9 + .../Types/StrokiMtsFile.cs | 9 + .../Types/StrokiMtsFileUrl.cs | 9 + .../Types/StrokiMtsFiles.cs | 12 + .../Types/StrokiMtsMultiItem.cs | 18 + ...Elib2Ebook.ExternalServices.TopLiba.csproj | 5 + .../Getters}/BiglibaGetter.cs | 10 +- .../Getters}/TopLibaGetter.cs | 10 +- .../Getters}/TopLibaGetterBase.cs | 11 +- ...ook.ExternalServices.TwilightRussia.csproj | 5 + .../TwilightRussiaGetter.cs | 9 +- ...Elib2Ebook.ExternalServices.Wattpad.csproj | 5 + .../Types/WattpadInfo.cs | 21 + .../Types/WattpadMeta.cs | 6 + .../Types/WattpadPart.cs | 17 + .../Types/WattpadUser.cs | 9 + .../WattpadGetter.cs | 13 +- ...b2Ebook.ExternalServices.WuxiaWorld.csproj | 5 + .../Types/WuxiaWorld/WuxiaWorldToc.cs | 9 + .../WuxiaWorldGetter.cs | 13 +- ...ok.ExternalServices.Younettranslate.csproj | 5 + .../YounettranslateGetter.cs | 11 +- .../Extensions/ImageFormatExtensionsTests.cs | 24 - tests/Core.Tests/Types/Book/BookTypeTests.cs | 165 --- .../Elib2Ebook.DomainServices.Tests.csproj} | 14 +- .../Extensions/HtmlDocumentExtensionsTests.cs | 4 +- .../Extensions/StreamExtensionTests.cs | 4 +- .../StringBuilderExtensionsTests.cs | 4 +- .../Extensions/StringExtensionsTests.cs | 4 +- .../Extensions/UriExtensionTests.cs | 4 +- .../Logic/Builders/BuilderTests.cs | 6 +- .../Misc/MiscTests.cs | 11 +- .../Misc/TempFolderTests.cs | 4 +- 542 files changed, 4548 insertions(+), 2811 deletions(-) create mode 100644 ARCHITECTURE.md delete mode 100644 src/Core/Logic/Getters/RanobeNovelsGetter.cs delete mode 100644 src/Core/Misc/GetterProvider.cs delete mode 100644 src/Core/Types/AuthorToday/AuthorTodayBookDetails.cs delete mode 100644 src/Core/Types/AuthorToday/AuthorTodayImage.cs delete mode 100644 src/Core/Types/AuthorToday/AuthorTodayUser.cs delete mode 100644 src/Core/Types/BookYandex/BooksYandexAudioBitrate.cs delete mode 100644 src/Core/Types/BookYandex/BooksYandexAudioOffline.cs delete mode 100644 src/Core/Types/BookYandex/BooksYandexAudioTrack.cs delete mode 100644 src/Core/Types/BookYandex/BooksYandexAuthor.cs delete mode 100644 src/Core/Types/BookYandex/BooksYandexBook.cs delete mode 100644 src/Core/Types/BookYandex/BooksYandexComicContent.cs delete mode 100644 src/Core/Types/BookYandex/BooksYandexComicMetadata.cs delete mode 100644 src/Core/Types/BookYandex/BooksYandexComicPage.cs delete mode 100644 src/Core/Types/BookYandex/BooksYandexCover.cs delete mode 100644 src/Core/Types/BookYandex/BooksYandexEpisode.cs delete mode 100644 src/Core/Types/BookYandex/BooksYandexEpisodes.cs delete mode 100644 src/Core/Types/BookYandex/BooksYandexPlaylist.cs delete mode 100644 src/Core/Types/BookYandex/BooksYandexResponse.cs delete mode 100644 src/Core/Types/BookYandex/BooksYandexUri.cs delete mode 100644 src/Core/Types/Bookriver/BookRiverApiResponse.cs delete mode 100644 src/Core/Types/Bookriver/BookRiverAuthResponse.cs delete mode 100644 src/Core/Types/Bookriver/BookRiverBook.cs delete mode 100644 src/Core/Types/Bookriver/BookRiverChapter.cs delete mode 100644 src/Core/Types/Bookriver/BookRiverChapterContent.cs delete mode 100644 src/Core/Types/Bookriver/BookRiverCurrentBook.cs delete mode 100644 src/Core/Types/Bookstab/BookstabApiResponse.cs delete mode 100644 src/Core/Types/Bookstab/BookstabBook.cs delete mode 100644 src/Core/Types/Bookstab/BookstabChapter.cs delete mode 100644 src/Core/Types/Bookstab/BookstabUser.cs delete mode 100644 src/Core/Types/Bookstime/BookstimeAuthResponse.cs delete mode 100644 src/Core/Types/Boosty/BoostyApiResponse.cs delete mode 100644 src/Core/Types/Boosty/BoostyExtra.cs delete mode 100644 src/Core/Types/Boosty/BoostyPost.cs delete mode 100644 src/Core/Types/DarkNovels/DarkNovelsAuthData.cs delete mode 100644 src/Core/Types/DarkNovels/DarkNovelsAuthToken.cs delete mode 100644 src/Core/Types/DarkNovels/DarkNovelsChapter.cs delete mode 100644 src/Core/Types/DarkNovels/DarkNovelsData.cs delete mode 100644 src/Core/Types/Dreame/DreameApiResponse.cs delete mode 100644 src/Core/Types/Dreame/DreameCatalog.cs delete mode 100644 src/Core/Types/Dreame/DreameChapter.cs delete mode 100644 src/Core/Types/Dreame/DreameNovel.cs delete mode 100644 src/Core/Types/Dreame/DreamePager.cs delete mode 100644 src/Core/Types/FanFicus/FanFicusApiResponse.cs delete mode 100644 src/Core/Types/FanFicus/FanFicusBook.cs delete mode 100644 src/Core/Types/FanFicus/FanFicusChapter.cs delete mode 100644 src/Core/Types/FanFicus/FanFicusCreator.cs delete mode 100644 src/Core/Types/FanFicus/FanFicusImage.cs delete mode 100644 src/Core/Types/FanFicus/FanFicusPart.cs delete mode 100644 src/Core/Types/FanFicus/FanFicusUser.cs delete mode 100644 src/Core/Types/Freedlit/FreedlitApiResponse.cs delete mode 100644 src/Core/Types/Freedlit/FreedlitApp.cs delete mode 100644 src/Core/Types/Freedlit/FreedlitAuthError.cs delete mode 100644 src/Core/Types/Freedlit/FreedlitAuthor.cs delete mode 100644 src/Core/Types/Freedlit/FreedlitBook.cs delete mode 100644 src/Core/Types/Freedlit/FreedlitChapter.cs delete mode 100644 src/Core/Types/Freedlit/FreedlitItemsContent.cs delete mode 100644 src/Core/Types/Freedlit/FreedlitProps.cs delete mode 100644 src/Core/Types/Freedlit/FreedlitSuccessResponse.cs delete mode 100644 src/Core/Types/HotNovelPub/HotNovelPubApiResponse.cs delete mode 100644 src/Core/Types/HotNovelPub/HotNovelPubAuthorize.cs delete mode 100644 src/Core/Types/HotNovelPub/HotNovelPubBook.cs delete mode 100644 src/Core/Types/HotNovelPub/HotNovelPubBookResponse.cs delete mode 100644 src/Core/Types/HotNovelPub/HotNovelPubChapter.cs delete mode 100644 src/Core/Types/Librebook/LibrebookAuthResponse.cs delete mode 100644 src/Core/Types/Litexit/LitexitChapter.cs delete mode 100644 src/Core/Types/Litexit/LitexitUser.cs delete mode 100644 src/Core/Types/Litgorod/LitgorodAuthResponse.cs delete mode 100644 src/Core/Types/Litgorod/LitgorodBookPart.cs delete mode 100644 src/Core/Types/Litgorod/LitgorodChapter.cs delete mode 100644 src/Core/Types/Litmarket/AuthResponse.cs delete mode 100644 src/Core/Types/Litmarket/Block.cs delete mode 100644 src/Core/Types/Litmarket/Chunk.cs delete mode 100644 src/Core/Types/Litmarket/LBook.cs delete mode 100644 src/Core/Types/Litmarket/Mod.cs delete mode 100644 src/Core/Types/Litmarket/Response.cs delete mode 100644 src/Core/Types/Litnet/LitnetAuthResponse.cs delete mode 100644 src/Core/Types/Litnet/LitnetBookResponse.cs delete mode 100644 src/Core/Types/Litnet/LitnetChapterResponse.cs delete mode 100644 src/Core/Types/Litnet/LitnetContentsResponse.cs delete mode 100644 src/Core/Types/Litres/Requests/LitresAuthData.cs delete mode 100644 src/Core/Types/Litres/Requests/LitresBrowseArtsData.cs delete mode 100644 src/Core/Types/Litres/Requests/LitresRequestBase.cs delete mode 100644 src/Core/Types/Litres/Response/LitresArt.cs delete mode 100644 src/Core/Types/Litres/Response/LitresArtTypeEnum.cs delete mode 100644 src/Core/Types/Litres/Response/LitresAuthResponseData.cs delete mode 100644 src/Core/Types/Litres/Response/LitresFile.cs delete mode 100644 src/Core/Types/Litres/Response/LitresFiles.cs delete mode 100644 src/Core/Types/Litres/Response/LitresMe.cs delete mode 100644 src/Core/Types/Litres/Response/LitresPartnerSubscriptions.cs delete mode 100644 src/Core/Types/Litres/Response/LitresPerson.cs delete mode 100644 src/Core/Types/Litres/Response/LitresResponse.cs delete mode 100644 src/Core/Types/Litres/Response/LitresSeria.cs delete mode 100644 src/Core/Types/Litres/Response/LitresStaticResponse.cs delete mode 100644 src/Core/Types/Litres/Response/LitresSubscription.cs delete mode 100644 src/Core/Types/Litsovet/LitsovetLoginResponse.cs delete mode 100644 src/Core/Types/MangaLib/MangaLibPg.cs delete mode 100644 src/Core/Types/MyBook/MyBookAuth.cs delete mode 100644 src/Core/Types/MyBook/MyBookAuthor.cs delete mode 100644 src/Core/Types/MyBook/MyBookBook.cs delete mode 100644 src/Core/Types/MyBook/MyBookFile.cs delete mode 100644 src/Core/Types/MyBook/MyBookMapFile.cs delete mode 100644 src/Core/Types/Neobook/NeobookAttachment.cs delete mode 100644 src/Core/Types/Neobook/NeobookAuth.cs delete mode 100644 src/Core/Types/Neobook/NeobookBook.cs delete mode 100644 src/Core/Types/Neobook/NeobookChapter.cs delete mode 100644 src/Core/Types/Neobook/NeobookChapterData.cs delete mode 100644 src/Core/Types/Neobook/NeobookLogin.cs delete mode 100644 src/Core/Types/Neobook/NeobookPostData.cs delete mode 100644 src/Core/Types/Neobook/NeobookTocChapter.cs delete mode 100644 src/Core/Types/Neobook/NeobookUser.cs delete mode 100644 src/Core/Types/Ranobe/RanobeBook.cs delete mode 100644 src/Core/Types/Ranobe/RanobeChapter.cs delete mode 100644 src/Core/Types/Ranobe/RanobeChapterContent.cs delete mode 100644 src/Core/Types/Ranobe/RanobeChapterShort.cs delete mode 100644 src/Core/Types/Ranobe/RanobeImage.cs delete mode 100644 src/Core/Types/RanobeHub/RanobeHubApiResponse.cs delete mode 100644 src/Core/Types/RanobeHub/RanobeHubChapter.cs delete mode 100644 src/Core/Types/RanobeHub/RanobeHubVolume.cs delete mode 100644 src/Core/Types/RanobeNovels/RanobeNovelsChapter.cs delete mode 100644 src/Core/Types/RanobeOvh/RanobeOvhBranch.cs delete mode 100644 src/Core/Types/RanobeOvh/RanobeOvhChapterFull.cs delete mode 100644 src/Core/Types/RanobeOvh/RanobeOvhManga.cs delete mode 100644 src/Core/Types/RanobeOvh/RanobeOvhMangaName.cs delete mode 100644 src/Core/Types/RanobeOvh/RanobeOvhPage.cs delete mode 100644 src/Core/Types/RanobeOvh/RanoveOvhTranslator.cs delete mode 100644 src/Core/Types/Ranobes/RanobesCookie.cs delete mode 100644 src/Core/Types/Renovels/RenovelsApiResponse.cs delete mode 100644 src/Core/Types/Renovels/RenovelsAuthResponse.cs delete mode 100644 src/Core/Types/Renovels/RenovelsBranch.cs delete mode 100644 src/Core/Types/Renovels/RenovelsChapter.cs delete mode 100644 src/Core/Types/Renovels/RenovelsContent.cs delete mode 100644 src/Core/Types/Renovels/RenovelsPage.cs delete mode 100644 src/Core/Types/Renovels/RenovelsPublisher.cs delete mode 100644 src/Core/Types/Renovels/RenovelsTocResponse.cs delete mode 100644 src/Core/Types/Rulate/RulateAuthResponse.cs delete mode 100644 src/Core/Types/Rulate/RulateChapter.cs delete mode 100644 src/Core/Types/SocialLib/LibSocialToken.cs delete mode 100644 src/Core/Types/SocialLib/RanobeLibBookDetail.cs delete mode 100644 src/Core/Types/SocialLib/SocialLibBookChapters.cs delete mode 100644 src/Core/Types/SocialLib/SocialLibChapter.cs delete mode 100644 src/Core/Types/SocialLib/SocialLibChapterOld.cs delete mode 100644 src/Core/Types/SocialLib/SocialLibChapters.cs delete mode 100644 src/Core/Types/SocialLib/User.cs delete mode 100644 src/Core/Types/SocialLib/WindowData.cs delete mode 100644 src/Core/Types/StrokiMts/StrokiMtsApiMultiResponse.cs delete mode 100644 src/Core/Types/StrokiMts/StrokiMtsApiResponse.cs delete mode 100644 src/Core/Types/StrokiMts/StrokiMtsAuthor.cs delete mode 100644 src/Core/Types/StrokiMts/StrokiMtsBookItem.cs delete mode 100644 src/Core/Types/StrokiMts/StrokiMtsContentInfo.cs delete mode 100644 src/Core/Types/StrokiMts/StrokiMtsFile.cs delete mode 100644 src/Core/Types/StrokiMts/StrokiMtsFileUrl.cs delete mode 100644 src/Core/Types/StrokiMts/StrokiMtsFiles.cs delete mode 100644 src/Core/Types/StrokiMts/StrokiMtsMultiItem.cs delete mode 100644 src/Core/Types/Wattpad/WattpadInfo.cs delete mode 100644 src/Core/Types/Wattpad/WattpadMeta.cs delete mode 100644 src/Core/Types/Wattpad/WattpadPart.cs delete mode 100644 src/Core/Types/Wattpad/WattpadUser.cs delete mode 100644 src/Core/Types/WuxiaWorld/WuxiaWorldToc.cs rename src/{Core/Types => Elib2Ebook.Domain}/Book/AdditionalFileCollection.cs (92%) rename src/{Core/Misc => Elib2Ebook.Domain/Book}/AdditionalTypeEnum.cs (72%) rename src/{Core/Types => Elib2Ebook.Domain}/Book/Author.cs (90%) rename src/{Core/Types => Elib2Ebook.Domain}/Book/Book.cs (95%) rename src/{Core/Types => Elib2Ebook.Domain}/Book/Chapter.cs (89%) rename src/{Core/Types => Elib2Ebook.Domain}/Book/Seria.cs (91%) rename src/{Core/Types => Elib2Ebook.Domain}/Common/IdChapter.cs (58%) rename src/{Core/Types => Elib2Ebook.Domain}/Common/TempFile.cs (98%) rename src/{Core/Types => Elib2Ebook.Domain}/Common/UrlChapter.cs (58%) create mode 100644 src/Elib2Ebook.Domain/Elib2Ebook.Domain.csproj rename src/{Core/Logic => Elib2Ebook.DomainServices}/BookNameBuilder.cs (86%) rename src/{Core/Logic => Elib2Ebook.DomainServices}/Builders/AdditionaFileBuilder.cs (95%) rename src/{Core/Logic => Elib2Ebook.DomainServices}/Builders/BuilderBase.cs (93%) rename src/{Core/Logic => Elib2Ebook.DomainServices}/Builders/CbzBuilder.cs (89%) rename src/{Core/Logic => Elib2Ebook.DomainServices}/Builders/EpubBuilder.cs (95%) rename src/{Core/Logic => Elib2Ebook.DomainServices}/Builders/Fb2Builder.cs (98%) rename src/{Core/Logic => Elib2Ebook.DomainServices}/Builders/JsonBuilder.cs (82%) rename src/{Core/Logic => Elib2Ebook.DomainServices}/Builders/JsonLiteBuilder.cs (94%) rename src/{Core/Logic => Elib2Ebook.DomainServices}/Builders/TxtBuilder.cs (88%) rename src/{Core => Elib2Ebook.DomainServices}/Configs/BookGetterConfig.cs (97%) rename src/{Core => Elib2Ebook.DomainServices}/Configs/Options.cs (81%) rename src/{Core/Core.csproj => Elib2Ebook.DomainServices/Elib2Ebook.DomainServices.csproj} (68%) rename src/{Core => Elib2Ebook.DomainServices}/Extensions/FileProviderExtensions.cs (95%) rename src/{Core => Elib2Ebook.DomainServices}/Extensions/HtmlDocumentExtensions.cs (97%) rename src/{Core => Elib2Ebook.DomainServices}/Extensions/HttpClientExtensions.cs (99%) rename src/{Core => Elib2Ebook.DomainServices}/Extensions/ImageFormatExtensions.cs (91%) rename src/{Core => Elib2Ebook.DomainServices}/Extensions/StreamExtension.cs (85%) rename src/{Core => Elib2Ebook.DomainServices}/Extensions/StringBuilderExtensions.cs (88%) rename src/{Core => Elib2Ebook.DomainServices}/Extensions/StringExtensions.cs (98%) rename src/{Core => Elib2Ebook.DomainServices}/Extensions/UriExtension.cs (97%) rename src/{Core => Elib2Ebook.DomainServices}/External/EpubSharp.deps.json (100%) rename src/{Core => Elib2Ebook.DomainServices}/External/EpubSharp.dll (100%) rename src/{Core => Elib2Ebook.DomainServices}/External/EpubSharp.pdb (100%) rename src/{Core/Logic => Elib2Ebook.DomainServices}/FileProvider.cs (94%) rename src/{Core/Logic => Elib2Ebook.DomainServices}/Getters/GetterBase.cs (98%) rename src/{Core => Elib2Ebook.DomainServices}/Misc/BuilderProvider.cs (84%) create mode 100644 src/Elib2Ebook.DomainServices/Misc/GetterFactory.cs rename src/{Core => Elib2Ebook.DomainServices}/Misc/TempFolder/TempFolder.cs (91%) rename src/{Core => Elib2Ebook.DomainServices}/Misc/TempFolder/TempFolderFactory.cs (91%) rename src/{Core => Elib2Ebook.DomainServices}/Patterns/ChapterPattern.xhtml (100%) rename src/{Core => Elib2Ebook.DomainServices}/Patterns/Roboto-Italic.ttf (100%) rename src/{Core => Elib2Ebook.DomainServices}/Patterns/Roboto-Regular.ttf (100%) rename src/{Core => Elib2Ebook.DomainServices}/Patterns/stylesheet.css (100%) rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Acomics}/AcomicsGetter.cs (92%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Acomics/Elib2Ebook.ExternalServices.Acomics.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.AuthorToday}/AuthorTodayGetter.cs (90%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Elib2Ebook.ExternalServices.AuthorToday.csproj rename src/{Core/Types/AuthorToday/AuthorTodayAuthResponse.cs => ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/AuthResponse.cs} (68%) rename src/{Core/Types/AuthorToday => ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types}/AuthorTodayChapter.cs (57%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/BookDetails.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/Image.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/User.cs rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.BookInBook}/BookInBookGetter.cs (94%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BookInBook/Elib2Ebook.ExternalServices.BookInBook.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.BookRiver}/BookRiverGetter.cs (91%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Elib2Ebook.ExternalServices.BookRiver.csproj create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/ApiResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/AuthResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/BookRiverBook.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/BookRiverChapter.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/BookRiverCurrentBook.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/ChapterContent.cs rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Bookinist}/BookinistGetter.cs (90%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Bookinist/Elib2Ebook.ExternalServices.Bookinist.csproj create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Elib2Ebook.ExternalServices.BooksYandex.csproj rename src/{Core/Logic/Getters/BooksYandex => ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Getters}/BooksYandexBooksGetter.cs (95%) rename src/{Core/Logic/Getters/BooksYandex => ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Getters}/BooksYandexComicsGetter.cs (82%) rename src/{Core/Logic/Getters/BooksYandex => ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Getters}/BooksYandexGetterBase.cs (92%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexAudioBitrate.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexAudioOffline.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexAudioTrack.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexAuthor.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexBook.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexComicContent.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexComicMetadata.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexComicPage.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexCover.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexEpisode.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexEpisodes.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexPlaylist.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexUri.cs rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Bookstab}/BookstabGetter.cs (84%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Elib2Ebook.ExternalServices.Bookstab.csproj create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Types/ApiResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Types/BookstabBook.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Types/BookstabChapter.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Types/User.cs rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Bookstime}/BookstimeGetter.cs (93%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Bookstime/Elib2Ebook.ExternalServices.Bookstime.csproj create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Bookstime/Types/AuthResponse.cs rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Bookuruk}/BookurukGetter.cs (95%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Bookuruk/Elib2Ebook.ExternalServices.Bookuruk.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Boosty}/BoostyGetter.cs (93%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/Elib2Ebook.ExternalServices.Boosty.csproj create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/Types/BoostyApiResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/Types/BoostyExtra.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/Types/BoostyPost.cs rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Boovel}/BoovelGetter.cs (93%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Boovel/Elib2Ebook.ExternalServices.Boovel.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.DarkNovels}/DarkNovelsGetter.cs (95%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Elib2Ebook.ExternalServices.DarkNovels.csproj create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Types/DarkNovelsAuthData.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Types/DarkNovelsAuthToken.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Types/DarkNovelsChapter.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Types/DarkNovelsData.cs rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Desu}/DesuGetter.cs (94%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Desu/Elib2Ebook.ExternalServices.Desu.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Dreame}/DreameGetter.cs (92%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Elib2Ebook.ExternalServices.Dreame.csproj create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreameApiResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreameCatalog.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreameChapter.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreameNovel.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreamePager.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Elib2Ebook.ExternalServices.FanFicus.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.FanFicus}/FanFicusGetter.cs (93%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusApiResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusBook.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusChapter.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusCreator.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusImage.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusPart.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusUser.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Fb2Top/Elib2Ebook.ExternalServices.Fb2Top.csproj rename src/{Core/Logic/Getters/Fb2Top => ExternalServices/Elib2Ebook.ExternalServices.Fb2Top/Getters}/Fb2TopGetter.cs (61%) rename src/{Core/Logic/Getters/Fb2Top => ExternalServices/Elib2Ebook.ExternalServices.Fb2Top/Getters}/Fb2TopGetterBase.cs (93%) rename src/{Core/Logic/Getters/Fb2Top => ExternalServices/Elib2Ebook.ExternalServices.Fb2Top/Getters}/LadyLibGetter.cs (61%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Ficbook/Elib2Ebook.ExternalServices.Ficbook.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Ficbook}/FicbookGetter.cs (94%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.FictionBook/Elib2Ebook.ExternalServices.FictionBook.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.FictionBook}/FictionBookGetter.cs (95%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Elib2Ebook.ExternalServices.Freedlit.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Freedlit}/FreedlitGetter.cs (94%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitApiResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitApp.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitAuthError.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitAuthor.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitBook.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitChapter.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitItemsContent.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitProps.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitSuccessResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Freedom/Elib2Ebook.ExternalServices.Freedom.csproj rename src/{Core/Logic/Getters/Freedom => ExternalServices/Elib2Ebook.ExternalServices.Freedom/Getters}/BookHamsterGetter.cs (62%) rename src/{Core/Logic/Getters/Freedom => ExternalServices/Elib2Ebook.ExternalServices.Freedom/Getters}/FreedomGetter.cs (61%) rename src/{Core/Logic/Getters/Freedom => ExternalServices/Elib2Ebook.ExternalServices.Freedom/Getters}/FreedomGetterBase.cs (92%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.HogwartsNet/Elib2Ebook.ExternalServices.HogwartsNet.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.HogwartsNet}/HogwartsNetGetter.cs (95%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Elib2Ebook.ExternalServices.HotNovelPub.csproj rename src/{Core/Logic/Getters/HotNovelPub => ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters}/EzNovelsGetter.cs (67%) rename src/{Core/Logic/Getters/HotNovelPub => ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters}/HotNovelPubGetter.cs (67%) rename src/{Core/Logic/Getters/HotNovelPub => ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters}/HotNovelPubGetterBase.cs (92%) rename src/{Core/Logic/Getters/HotNovelPub => ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters}/LaNovelsGetter.cs (67%) rename src/{Core/Logic/Getters/HotNovelPub => ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters}/LightNovelDailyGetter.cs (68%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubApiResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubAuthorize.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubBook.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubBookResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubChapter.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.HubBook/Elib2Ebook.ExternalServices.HubBook.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.HubBook}/HubBookGetter.cs (91%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.IGram/Elib2Ebook.ExternalServices.IGram.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.IGram}/IGramGetter.cs (94%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.IGram/Types/LitexitChapter.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.IGram/Types/LitexitUser.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Jaomix/Elib2Ebook.ExternalServices.Jaomix.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Jaomix}/JaomixGetter.cs (93%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Elib2Ebook.ExternalServices.LibSocial.csproj rename src/{Core/Logic/Getters/LibSocial => ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters}/HentaiLibGetter.cs (73%) rename src/{Core/Logic/Getters/LibSocial => ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters}/MangaLibGetter.cs (73%) rename src/{Core/Logic/Getters/LibSocial => ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters}/MangaLibGetterBase.cs (71%) rename src/{Core/Logic/Getters/LibSocial => ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters}/MangaLibOrgGetter.cs (74%) rename src/{Core/Logic/Getters/LibSocial => ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters}/NewLibSocialGetterBase.cs (97%) rename src/{Core/Logic/Getters/LibSocial => ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters}/RanobeLibGetter.cs (95%) rename src/{Core/Logic/Getters/LibSocial => ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters}/SlashLibGetter.cs (70%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/MangaLib/MangaLibPg.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/LibSocialToken.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/RanobeLibBookDetail.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibBookChapters.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibChapter.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibChapterOld.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibChapters.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/User.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/WindowData.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Libbox/Elib2Ebook.ExternalServices.Libbox.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Libbox}/LibboxGetter.cs (95%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Libking/Elib2Ebook.ExternalServices.Libking.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Libking}/LibkingGetter.cs (94%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Librebook/Elib2Ebook.ExternalServices.Librebook.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Librebook}/LibrebookGetter.cs (94%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Librebook/Types/LibrebookAuthResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Libst/Elib2Ebook.ExternalServices.Libst.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Libst}/LibstGetter.cs (95%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.LitLife/Elib2Ebook.ExternalServices.LitLife.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.LitLife}/LitLifeGetter.cs (95%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/Elib2Ebook.ExternalServices.Litgorod.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Litgorod}/LitgorodGetter.cs (95%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/Types/LitgorodAuthResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/Types/LitgorodBookPart.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/Types/LitgorodChapter.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Elib2Ebook.ExternalServices.Litmarket.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Litmarket}/LitmarketGetter.cs (96%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/AuthResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/Block.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/Chunk.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/LBook.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/Mod.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/Response.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litmir/Elib2Ebook.ExternalServices.Litmir.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Litmir}/LitmirGetter.cs (96%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Elib2Ebook.ExternalServices.Litnet.csproj rename src/{Core/Logic/Getters/Litnet => ExternalServices/Elib2Ebook.ExternalServices.Litnet/Getters}/BooknetComGetter.cs (62%) rename src/{Core/Logic/Getters/Litnet => ExternalServices/Elib2Ebook.ExternalServices.Litnet/Getters}/BooknetUaGetter.cs (62%) rename src/{Core/Logic/Getters/Litnet => ExternalServices/Elib2Ebook.ExternalServices.Litnet/Getters}/LitnetGetter.cs (61%) rename src/{Core/Logic/Getters/Litnet => ExternalServices/Elib2Ebook.ExternalServices.Litnet/Getters}/LitnetGetterBase.cs (97%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Types/LitnetAuthResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Types/LitnetBookResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Types/LitnetChapterResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Types/LitnetContentsResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Elib2Ebook.ExternalServices.Litres.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Litres}/LitresGetter.cs (97%) rename src/{Core/Types/Litres => ExternalServices/Elib2Ebook.ExternalServices.Litres/Types}/LitresBook.cs (61%) rename src/{Core/Types/Litres => ExternalServices/Elib2Ebook.ExternalServices.Litres/Types}/LitresPayload.cs (58%) rename src/{Core/Types/Litres => ExternalServices/Elib2Ebook.ExternalServices.Litres/Types}/LitresTarget.cs (59%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresAuthData.cs rename src/{Core/Types/Litres => ExternalServices/Elib2Ebook.ExternalServices.Litres/Types}/Requests/LitresAuthRequest.cs (56%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresBrowseArtsData.cs rename src/{Core/Types/Litres => ExternalServices/Elib2Ebook.ExternalServices.Litres/Types}/Requests/LitresBrowseArtsRequest.cs (55%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresRequestBase.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresArt.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresArtTypeEnum.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresAuthResponseData.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresFile.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresFiles.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresMe.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresPartnerSubscriptions.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresPerson.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresSeria.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresStaticResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresSubscription.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litsovet/Elib2Ebook.ExternalServices.Litsovet.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Litsovet}/LitsovetGetter.cs (93%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litsovet/Types/LitsovetLoginResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.MangaMammy/Elib2Ebook.ExternalServices.MangaMammy.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.MangaMammy}/MangaMammyGetter.cs (93%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.MirKnig/Elib2Ebook.ExternalServices.MirKnig.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.MirKnig}/MirKnigGetter.cs (92%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Elib2Ebook.ExternalServices.MyBook.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.MyBook}/MyBookGetter.cs (96%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookAuth.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookAuthor.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookBook.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookFile.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookMapFile.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Elib2Ebook.ExternalServices.Neobook.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Neobook}/NeobookGetter.cs (94%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookAttachment.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookAuth.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookBook.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookChapter.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookChapterData.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookLogin.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookPostData.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookTocChapter.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookUser.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.NovelTranslate/Elib2Ebook.ExternalServices.NovelTranslate.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.NovelTranslate}/NovelTranslateGetter.cs (94%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Novelhall/Elib2Ebook.ExternalServices.Novelhall.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Novelhall}/NovelhallGetter.cs (92%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Novelxo/Elib2Ebook.ExternalServices.Novelxo.csproj rename src/{Core/Logic/Getters/Novelxo => ExternalServices/Elib2Ebook.ExternalServices.Novelxo/Getters}/NovelxoGetter.cs (61%) rename src/{Core/Logic/Getters/Novelxo => ExternalServices/Elib2Ebook.ExternalServices.Novelxo/Getters}/NovelxoGetterBase.cs (96%) rename src/{Core/Logic/Getters/Novelxo => ExternalServices/Elib2Ebook.ExternalServices.Novelxo/Getters}/NovelxoRuGetter.cs (62%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.OnlineKnigi/Elib2Ebook.ExternalServices.OnlineKnigi.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.OnlineKnigi}/OnlineKnigiGetter.cs (91%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Prodaman/Elib2Ebook.ExternalServices.Prodaman.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Prodaman}/ProdamanGetter.cs (97%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Elib2Ebook.ExternalServices.Ranobe.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Ranobe}/RanobeGetter.cs (90%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeBook.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeChapter.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeChapterContent.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeChapterShort.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeImage.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/Elib2Ebook.ExternalServices.RanobeHub.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.RanobeHub}/RanobeHubGetter.cs (92%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/Types/RanobeHubApiResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/Types/RanobeHubChapter.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/Types/RanobeHubVolume.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Elib2Ebook.ExternalServices.RanobeOvh.csproj rename src/{Core/Logic/Getters/RanobeOvh => ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Getters}/MangaOvhGetter.cs (77%) rename src/{Core/Logic/Getters/RanobeOvh => ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Getters}/NovelOvhGetter.cs (95%) rename src/{Core/Logic/Getters/RanobeOvh => ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Getters}/RanobeOvhGetterBase.cs (92%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhBranch.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhChapterFull.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhManga.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhMangaName.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhPage.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanoveOvhTranslator.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.RanobesCom/Elib2Ebook.ExternalServices.RanobesCom.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.RanobesCom}/RanobesComGetter.cs (96%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.RanobesCom/Types/RanobesCookie.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.ReadManga/Elib2Ebook.ExternalServices.ReadManga.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.ReadManga}/ReadMangaGetter.cs (93%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Readli/Elib2Ebook.ExternalServices.Readli.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Readli}/ReadliGetter.cs (96%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Elib2Ebook.ExternalServices.Renovels.csproj rename src/{Core/Logic/Getters/Renovels => ExternalServices/Elib2Ebook.ExternalServices.Renovels/Getters}/RemangaGetter.cs (84%) rename src/{Core/Logic/Getters/Renovels => ExternalServices/Elib2Ebook.ExternalServices.Renovels/Getters}/RenovelsGetter.cs (65%) rename src/{Core/Logic/Getters/Renovels => ExternalServices/Elib2Ebook.ExternalServices.Renovels/Getters}/RenovelsGetterBase.cs (94%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsApiResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsAuthResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsBranch.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsChapter.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsContent.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsPage.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsPublisher.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsTocResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Romfant/Elib2Ebook.ExternalServices.Romfant.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Romfant}/RomfantGetter.cs (94%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.RoyalRoad/Elib2Ebook.ExternalServices.RoyalRoad.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.RoyalRoad}/RoyalRoadGetter.cs (92%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Elib2Ebook.ExternalServices.Rulate.csproj rename src/{Core/Logic/Getters/Rulate => ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters}/BllateGetter.cs (67%) rename src/{Core/Logic/Getters/Rulate => ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters}/ErolateGetter.cs (72%) rename src/{Core/Logic/Getters/Rulate => ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters}/MlateGetter.cs (71%) rename src/{Core/Logic/Getters/Rulate => ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters}/RulateGetter.cs (72%) rename src/{Core/Logic/Getters/Rulate => ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters}/RulateGetterBase.cs (94%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Types/RulateAuthResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Types/RulateChapter.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Samlib/Elib2Ebook.ExternalServices.Samlib.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Samlib}/SamlibGetter.cs (95%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Elib2Ebook.ExternalServices.StrokiMts.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.StrokiMts}/StrokiMtsGetter.cs (96%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsApiMultiResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsApiResponse.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsAuthor.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsBookItem.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsContentInfo.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsFile.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsFileUrl.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsFiles.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsMultiItem.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.TopLiba/Elib2Ebook.ExternalServices.TopLiba.csproj rename src/{Core/Logic/Getters/TopLiba => ExternalServices/Elib2Ebook.ExternalServices.TopLiba/Getters}/BiglibaGetter.cs (86%) rename src/{Core/Logic/Getters/TopLiba => ExternalServices/Elib2Ebook.ExternalServices.TopLiba/Getters}/TopLibaGetter.cs (86%) rename src/{Core/Logic/Getters/TopLiba => ExternalServices/Elib2Ebook.ExternalServices.TopLiba/Getters}/TopLibaGetterBase.cs (95%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.TwilightRussia/Elib2Ebook.ExternalServices.TwilightRussia.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.TwilightRussia}/TwilightRussiaGetter.cs (93%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Elib2Ebook.ExternalServices.Wattpad.csproj create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Types/WattpadInfo.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Types/WattpadMeta.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Types/WattpadPart.cs create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Types/WattpadUser.cs rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Wattpad}/WattpadGetter.cs (90%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.WuxiaWorld/Elib2Ebook.ExternalServices.WuxiaWorld.csproj create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.WuxiaWorld/Types/WuxiaWorld/WuxiaWorldToc.cs rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.WuxiaWorld}/WuxiaWorldGetter.cs (93%) create mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Younettranslate/Elib2Ebook.ExternalServices.Younettranslate.csproj rename src/{Core/Logic/Getters => ExternalServices/Elib2Ebook.ExternalServices.Younettranslate}/YounettranslateGetter.cs (93%) delete mode 100644 tests/Core.Tests/Extensions/ImageFormatExtensionsTests.cs delete mode 100644 tests/Core.Tests/Types/Book/BookTypeTests.cs rename tests/{Core.Tests/Core.Tests.csproj => Elib2Ebook.DomainServices.Tests/Elib2Ebook.DomainServices.Tests.csproj} (57%) rename tests/{Core.Tests => Elib2Ebook.DomainServices.Tests}/Extensions/HtmlDocumentExtensionsTests.cs (96%) rename tests/{Core.Tests => Elib2Ebook.DomainServices.Tests}/Extensions/StreamExtensionTests.cs (88%) rename tests/{Core.Tests => Elib2Ebook.DomainServices.Tests}/Extensions/StringBuilderExtensionsTests.cs (76%) rename tests/{Core.Tests => Elib2Ebook.DomainServices.Tests}/Extensions/StringExtensionsTests.cs (96%) rename tests/{Core.Tests => Elib2Ebook.DomainServices.Tests}/Extensions/UriExtensionTests.cs (95%) rename tests/{Core.Tests => Elib2Ebook.DomainServices.Tests}/Logic/Builders/BuilderTests.cs (88%) rename tests/{Core.Tests => Elib2Ebook.DomainServices.Tests}/Misc/MiscTests.cs (91%) rename tests/{Core.Tests => Elib2Ebook.DomainServices.Tests}/Misc/TempFolderTests.cs (96%) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 00000000..b8d4527f --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,235 @@ +# Архитектура проекта + +## Общая структура + +Проект разделён на три основных слоя: + +``` +Elib2Ebook.sln +├── src/ +│ ├── Elib2Ebook.Domain/ # Модели данных +│ ├── Elib2Ebook.DomainServices/ # Бизнес-логика +│ ├── Elib2EbookCli/ # Консольное приложение +│ ├── Elib2EbookWeb/ # Веб-приложение (Blazor) +│ └── ExternalServices/ # Интеграция с внешними сайтами +└── tests/ + └── Elib2Ebook.DomainServices.Tests/ +``` + +--- + +## 1. Elib2Ebook.Domain — Модели данных + +Ядро предметной области. Не имеет зависимостей от внешних сервисов. + +``` +Elib2Ebook.Domain/ +└── Book/ + ├── Book.cs # Книга (название, автор, обложка, главы, серия, язык) + ├── Author.cs # Автор (имя, ссылка) + ├── Chapter.cs # Глава (название, контент, изображения) + ├── Seria.cs # Серия книг (название, номер) + ├── AdditionalFileCollection.cs # Коллекция доп. файлов (аудио, книги, изображения) + └── AdditionalTypeEnum.cs # Типы доп. файлов: books, audio, images +└── Common/ + ├── TempFile.cs # Временный файл (скачанное изображение и т.п.) + ├── IdChapter.cs # Глава, идентифицируемая по числовому ID + └── UrlChapter.cs # Глава, идентифицируемая по URL +``` + +**Ключевые особенности:** +- `Book` реализует `IDisposable` — освобождает обложку и доп. файлы +- `TempFile` — абстракция временного файла на диске с автоматическим удалением +- `AdditionalFileCollection` — коллекция доп. файлов с группировкой по типу + +--- + +## 2. Elib2Ebook.DomainServices — Бизнес-логика + +Содержит всю логику обработки книг: загрузку, сборку, работу с файлами. + +``` +Elib2Ebook.DomainServices/ +├── Configs/ +│ ├── Options.cs # Параметры командной строки (через CommandLine) +│ └── BookGetterConfig.cs # Конфигурация для HTTP-клиента, прокси, Flaresolverr +├── Builders/ +│ ├── BuilderBase.cs # Абстрактный базовый класс для сборщиков +│ ├── EpubBuilder.cs # Сборка EPUB +│ ├── Fb2Builder.cs # Сборка FB2 +│ ├── CbzBuilder.cs # Сборка CBZ (Comic Book Archive) +│ ├── JsonBuilder.cs # Сборка JSON (полные данные) +│ ├── JsonLiteBuilder.cs # Сборка JSON (только текст) +│ ├── TxtBuilder.cs # Сборка TXT +│ └── AdditionaFileBuilder.cs # Сохранение дополнительных файлов +├── Getters/ +│ └── GetterBase.cs # Абстрактный базовый класс для загрузчиков +├── Misc/ +│ ├── GetterFactory.cs # Фабрика загрузчиков (reflection-based) +│ ├── BuilderProvider.cs # Фабрика сборщиков (switch по формату) +│ └── TempFolder/ # Управление временными файлами +├── Extensions/ # Методы расширения для различных типов +│ ├── HtmlDocumentExtensions.cs +│ ├── HttpClientExtensions.cs +│ ├── StreamExtension.cs +│ ├── StringBuilderExtensions.cs +│ ├── StringExtensions.cs +│ ├── UriExtension.cs +│ ├── FileProviderExtensions.cs +│ └── ImageFormatExtensions.cs +├── External/ # Встроенная копия EpubSharp.dll +├── Patterns/ # Шаблоны для EPUB (CSS, шрифты, xhtml) +├── BookNameBuilder.cs # Построитель имени файла по шаблону +└── FileProvider.cs # Провайдер файловой системы +``` + +### Архитектура сборщиков (Builders) + +``` +BuilderBase (abstract) +├── GetFileName(Book) → "Автор - Название.epub" +├── Build(Book) +│ ├── BuildInternal(Book, fileName) — специфичная для формата сборка +│ └── SaveCover() — если опция --cover +│ +├── EpubBuilder — создаёт EPUB через EpubSharp + CSS/шрифты +├── Fb2Builder — создаёт FB2 через XDocument +├── CbzBuilder — создаёт CBZ (zip-архив с изображениями) +├── JsonBuilder — сериализует Book в JSON с полными данными +├── JsonLiteBuilder — сериализует только текст + заголовки глав +└── TxtBuilder — создаёт простой текстовый файл +``` + +### Архитектура загрузчиков (Getters) + +``` +GetterBase (abstract) +├── SystemUrl — базовый URL сайта +├── IsSameUrl(url) — проверка поддержки URL +├── Init() — инициализация (куки, заголовки) +├── Authorize() — авторизация на сайте +├── Get(url) → Book — загрузка книги +│ +└── Внешние реализации (через GetterFactory): + Elib2Ebook.ExternalServices.* +``` + +**Как загрузчик определяет свою привязку к сайту:** + +Каждый загрузчик переопределяет `SystemUrl`. Например: +```csharp +protected override Uri SystemUrl => new("https://author.today/"); +``` + +`GetterFactory.Get()` через рефлексию находит все публичные неабстрактные классы, наследующие `GetterBase`, и вызывает `IsSameUrl()` для каждого. Первый подошедший возвращается. + +--- + +## 3. ExternalServices — Внешние сервисы + +Каждый внешний сайт реализован как отдельная сборка (проект) в папке `ExternalServices`. Все сборки загружаются динамически через рефлексию в `GetterFactory`. + +``` +ExternalServices/ +├── Elib2Ebook.ExternalServices.AuthorToday/ — author.today +├── Elib2Ebook.ExternalServices.Litres/ — litres.ru +├── Elib2Ebook.ExternalServices.Litnet/ — litnet.com +├── Elib2Ebook.ExternalServices.LibSocial/ — mangalib.me, ranobelib.me, hentailib.me, v2.slashlib.me и др. +├── Elib2Ebook.ExternalServices.BooksYandex/ — books.yandex.ru (комиксы и книги) +├── Elib2Ebook.ExternalServices.Boosty/ — boosty.to +└── ... (всего 60+ проектов) +``` + +**Особенности:** +- Каждый проект содержит `*Getter.cs` — класс, наследующий `GetterBase` +- Проекты могут содержать подпапки `Types/` с DTO для API +- Некоторые проекты содержат несколько геттеров (например, `LibSocial` — общая кодовая база для семейства сайтов) + +--- + +## 4. Elib2EbookCli — Консольное приложение + +Точка входа `Program.cs`: + +``` +Parse args (CommandLine) + └→ Создать BookGetterConfig (HTTP-клиент, куки, прокси, Flaresolverr) + └→ GetterFactory.Get() — получить загрузчик по URL + ├── Init() + ├── Authorize() + └── Get(url) → Book + └→ Для каждого формата: + BuilderProvider.Get(format) → BuilderBase + └── Build(Book) +``` + +**Поток выполнения:** +1. Парсинг аргументов командной строки +2. Создание `BookGetterConfig` — конфигурации с HTTP-клиентом +3. Получение загрузчика через `GetterFactory.Get()` (reflection-based) +4. Инициализация загрузчика (куки, заголовки) +5. Авторизация (если указаны логин/пароль) +6. Для каждого URL: + 1. Загрузка книги через `getter.Get(url)` + 2. Для каждого формата: сборка файла через `BuilderProvider.Get(format).Build(book)` +7. Сохранение дополнительных файлов (если `--additional`) + +--- + +## 5. Elib2EbookWeb — Веб-приложение (Blazor) + +Blazor Static SSR приложение. + +``` +Elib2EbookWeb/ +├── Components/ +│ ├── App.razor # Корневой компонент +│ ├── Routes.razor # Маршрутизация +│ ├── BookForm.razor # Форма ввода URL и параметров +│ ├── ShortBook.razor # Компонент для отображения краткой информации +│ └── PWA.razor # PWA-компонент +├── Misc/ +│ └── SbWriter.cs # StringBuilder writer для веб-вывода +└── wwwroot/ + ├── app.css # Стили + ├── favicon.png # Иконка + ├── manifest.json # PWA-манифест + └── bootstrap/ # Bootstrap CSS +``` + +**Особенности:** +- Использует Blazor Static SSR (без интерактивности на клиенте) +- Страница отправляет POST-запрос с параметрами и получает готовый файл +- Поддерживает PWA для установки как приложения + +--- + +## 6. Конфигурация сети + +`BookGetterConfig` создаёт и настраивает `HttpClient`: +- **Базовый Handler**: поддержка cookies, автоматические редиректы +- **Прокси**: HTTP/HTTPS, SOCKS4, SOCKS5 +- **Flaresolverr**: перенаправление запросов через Flaresolverr для обхода Cloudflare +- **Таймаут**: настраиваемый (по умолчанию 120с) +- **Заголовки**: User-Agent, Referer и др. + +--- + +## 7. Основные паттерны + +| Паттерн | Применение | +|---------|-----------| +| **Factory Method** | `GetterFactory` — создание загрузчика по URL через рефлексию | +| **Strategy** | `BuilderBase` — разные форматы сборки через единый интерфейс | +| **Template Method** | `BuilderBase.Build()` → `BuildInternal()` — общая логика сохранения + специфичная сборка | +| **Abstract Factory** | `BuilderProvider` — выбор сборщика по строке формата | + +--- + +## 8. Сборка (Docker) + +Проект публикуется в два Docker-образа: +- `onlyfart/elib2ebookcli` — CLI версия +- `onlyfart/elib2ebookweb` — Web версия (Blazor) + +Оба собираются через GitHub Actions (см. `.github/workflows/dotnet.yml`). Для CLI используется **ReadyToRun + SingleFile** для portable-сборок без необходимости установки .NET Runtime. diff --git a/Directory.Packages.props b/Directory.Packages.props index 2d326696..2ebf25a3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,6 +6,8 @@ + + diff --git a/Elib2Ebook.sln b/Elib2Ebook.sln index 0d2a4ac9..e1311e02 100644 --- a/Elib2Ebook.sln +++ b/Elib2Ebook.sln @@ -8,15 +8,145 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "common", "common", "{D12CC1 Directory.Packages.props = Directory.Packages.props EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "src\Core\Core.csproj", "{3576A033-7429-4DE3-BBE3-CD703A4564ED}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2EbookWeb", "src\Elib2EbookWeb\Elib2EbookWeb.csproj", "{C3CCD71A-37D0-4665-A628-AC6185313A4C}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2EbookCli", "src\Elib2EbookCli\Elib2EbookCli.csproj", "{04A5E9A6-9EA9-4771-9766-1241F8341A9B}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0C88DD14-F956-CE84-757C-A364CCF449FC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Tests", "tests\Core.Tests\Core.Tests.csproj", "{97B6CA29-9704-4A44-8879-E7015403E743}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.DomainServices.Tests", "tests\Elib2Ebook.DomainServices.Tests\Elib2Ebook.DomainServices.Tests.csproj", "{97B6CA29-9704-4A44-8879-E7015403E743}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ExternalServices", "ExternalServices", "{EC9257F5-F59F-4F7C-B316-A153DCB9501C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Acomics", "src\ExternalServices\Elib2Ebook.ExternalServices.Acomics\Elib2Ebook.ExternalServices.Acomics.csproj", "{4FBF6A86-DE1B-45E3-A1CE-B1271E927DF8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.AuthorToday", "src\ExternalServices\Elib2Ebook.ExternalServices.AuthorToday\Elib2Ebook.ExternalServices.AuthorToday.csproj", "{898F86C5-1886-41BE-97DD-46A182261678}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.BookInBook", "src\ExternalServices\Elib2Ebook.ExternalServices.BookInBook\Elib2Ebook.ExternalServices.BookInBook.csproj", "{88836B48-12C3-4CC7-A73E-0245970589CB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.BookRiver", "src\ExternalServices\Elib2Ebook.ExternalServices.BookRiver\Elib2Ebook.ExternalServices.BookRiver.csproj", "{297D901A-F3D0-4653-B4EB-CBD3D4402612}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Bookinist", "src\ExternalServices\Elib2Ebook.ExternalServices.Bookinist\Elib2Ebook.ExternalServices.Bookinist.csproj", "{82456D25-7753-4D1C-9ADC-450948B7FAC0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.BooksYandex", "src\ExternalServices\Elib2Ebook.ExternalServices.BooksYandex\Elib2Ebook.ExternalServices.BooksYandex.csproj", "{E5408C31-60C9-4A60-89D9-840C8C9E5E2F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Bookstab", "src\ExternalServices\Elib2Ebook.ExternalServices.Bookstab\Elib2Ebook.ExternalServices.Bookstab.csproj", "{79094B35-C3C6-4332-8A79-6E3D32582059}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Bookstime", "src\ExternalServices\Elib2Ebook.ExternalServices.Bookstime\Elib2Ebook.ExternalServices.Bookstime.csproj", "{7311835B-6CE8-470E-AFB4-B76424A86096}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Bookuruk", "src\ExternalServices\Elib2Ebook.ExternalServices.Bookuruk\Elib2Ebook.ExternalServices.Bookuruk.csproj", "{04F673E8-6A3D-4A60-A829-1B851FDB5A4C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Boosty", "src\ExternalServices\Elib2Ebook.ExternalServices.Boosty\Elib2Ebook.ExternalServices.Boosty.csproj", "{1826ECD1-D177-4AEF-B821-8B65535CAA46}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Boovel", "src\ExternalServices\Elib2Ebook.ExternalServices.Boovel\Elib2Ebook.ExternalServices.Boovel.csproj", "{C6D9607E-AB00-4E29-A8A9-8F378B2D755B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.DarkNovels", "src\ExternalServices\Elib2Ebook.ExternalServices.DarkNovels\Elib2Ebook.ExternalServices.DarkNovels.csproj", "{19462AF9-35D7-43C8-AFA1-39E2FD1C7EBD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Desu", "src\ExternalServices\Elib2Ebook.ExternalServices.Desu\Elib2Ebook.ExternalServices.Desu.csproj", "{745684DE-BABB-4BAA-BC36-DA3029E8C99F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Dreame", "src\ExternalServices\Elib2Ebook.ExternalServices.Dreame\Elib2Ebook.ExternalServices.Dreame.csproj", "{F58C0BE6-D91A-42F7-A221-4A882911E61C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.FanFicus", "src\ExternalServices\Elib2Ebook.ExternalServices.FanFicus\Elib2Ebook.ExternalServices.FanFicus.csproj", "{5F1D96A7-8252-4D60-AE6E-A66C045A8A92}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Fb2Top", "src\ExternalServices\Elib2Ebook.ExternalServices.Fb2Top\Elib2Ebook.ExternalServices.Fb2Top.csproj", "{AEDD4AE8-A831-46EE-AF49-EAC3183D5971}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Ficbook", "src\ExternalServices\Elib2Ebook.ExternalServices.Ficbook\Elib2Ebook.ExternalServices.Ficbook.csproj", "{F2387E3F-8B78-4FAC-BDAA-A3E2EBC4D889}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.FictionBook", "src\ExternalServices\Elib2Ebook.ExternalServices.FictionBook\Elib2Ebook.ExternalServices.FictionBook.csproj", "{D509243A-E5AA-430D-B47E-8ADC5DA31125}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Freedlit", "src\ExternalServices\Elib2Ebook.ExternalServices.Freedlit\Elib2Ebook.ExternalServices.Freedlit.csproj", "{7C8AA7A4-E72E-4A21-BC13-D7BA5A2219DC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Freedom", "src\ExternalServices\Elib2Ebook.ExternalServices.Freedom\Elib2Ebook.ExternalServices.Freedom.csproj", "{443B5C89-0904-4AE8-B48F-1E92709FBEF3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.HogwartsNet", "src\ExternalServices\Elib2Ebook.ExternalServices.HogwartsNet\Elib2Ebook.ExternalServices.HogwartsNet.csproj", "{48EA13C9-7FBC-4DCA-85CF-CCB1493A73C8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.HotNovelPub", "src\ExternalServices\Elib2Ebook.ExternalServices.HotNovelPub\Elib2Ebook.ExternalServices.HotNovelPub.csproj", "{EF1817C6-1FEB-4185-94F3-6BDB9ED1660A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.HubBook", "src\ExternalServices\Elib2Ebook.ExternalServices.HubBook\Elib2Ebook.ExternalServices.HubBook.csproj", "{1EF56286-3151-4D13-B662-C23CBE7148FE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.IGram", "src\ExternalServices\Elib2Ebook.ExternalServices.IGram\Elib2Ebook.ExternalServices.IGram.csproj", "{5FF3DBB0-B465-46C5-ADB7-CE8998A6E7BA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Jaomix", "src\ExternalServices\Elib2Ebook.ExternalServices.Jaomix\Elib2Ebook.ExternalServices.Jaomix.csproj", "{F743054C-0D7F-4352-9DBD-365C4CFA19C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.LibSocial", "src\ExternalServices\Elib2Ebook.ExternalServices.LibSocial\Elib2Ebook.ExternalServices.LibSocial.csproj", "{509EA8AB-5717-4953-87FC-F3EDD041F200}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Libbox", "src\ExternalServices\Elib2Ebook.ExternalServices.Libbox\Elib2Ebook.ExternalServices.Libbox.csproj", "{A9677606-014A-43A4-83D1-7871D1547862}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Libking", "src\ExternalServices\Elib2Ebook.ExternalServices.Libking\Elib2Ebook.ExternalServices.Libking.csproj", "{42B441C3-43AD-4D8F-96E0-547D7257FB04}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Librebook", "src\ExternalServices\Elib2Ebook.ExternalServices.Librebook\Elib2Ebook.ExternalServices.Librebook.csproj", "{DD4CFE16-3F58-4AA0-A995-312AFC55F0B3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Libst", "src\ExternalServices\Elib2Ebook.ExternalServices.Libst\Elib2Ebook.ExternalServices.Libst.csproj", "{CC31B699-7B15-4685-8E00-01E2DE6CA88B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.LitLife", "src\ExternalServices\Elib2Ebook.ExternalServices.LitLife\Elib2Ebook.ExternalServices.LitLife.csproj", "{ED59B9E2-82EC-4984-9A27-E9DFF3B13B60}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Litgorod", "src\ExternalServices\Elib2Ebook.ExternalServices.Litgorod\Elib2Ebook.ExternalServices.Litgorod.csproj", "{6FC8F397-8F3D-4300-A8B3-6F76B68C959B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Litmarket", "src\ExternalServices\Elib2Ebook.ExternalServices.Litmarket\Elib2Ebook.ExternalServices.Litmarket.csproj", "{7B9E64D1-70C9-4462-BE0B-A965A2825919}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Litmir", "src\ExternalServices\Elib2Ebook.ExternalServices.Litmir\Elib2Ebook.ExternalServices.Litmir.csproj", "{82D6EBAF-B0B6-4744-A556-E5667DF823B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Litnet", "src\ExternalServices\Elib2Ebook.ExternalServices.Litnet\Elib2Ebook.ExternalServices.Litnet.csproj", "{410366DF-4121-44DE-923D-218ACBA2F8CC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Litres", "src\ExternalServices\Elib2Ebook.ExternalServices.Litres\Elib2Ebook.ExternalServices.Litres.csproj", "{A8E066A4-00E7-4209-BB1E-0BD9A2FBAC8E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Litsovet", "src\ExternalServices\Elib2Ebook.ExternalServices.Litsovet\Elib2Ebook.ExternalServices.Litsovet.csproj", "{656422FD-47DA-4F06-AB76-57FF1F77B040}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.MangaMammy", "src\ExternalServices\Elib2Ebook.ExternalServices.MangaMammy\Elib2Ebook.ExternalServices.MangaMammy.csproj", "{502B8111-F428-4584-AE44-D1AD3B4F16B5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.MirKnig", "src\ExternalServices\Elib2Ebook.ExternalServices.MirKnig\Elib2Ebook.ExternalServices.MirKnig.csproj", "{174CF08D-E13D-4904-90C0-CC740C9DE086}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.MyBook", "src\ExternalServices\Elib2Ebook.ExternalServices.MyBook\Elib2Ebook.ExternalServices.MyBook.csproj", "{853E3B54-DB8A-4A82-8565-E6103130DA3C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Neobook", "src\ExternalServices\Elib2Ebook.ExternalServices.Neobook\Elib2Ebook.ExternalServices.Neobook.csproj", "{B8DC7FD1-782F-4246-B096-836D82B29A03}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.NovelTranslate", "src\ExternalServices\Elib2Ebook.ExternalServices.NovelTranslate\Elib2Ebook.ExternalServices.NovelTranslate.csproj", "{8AE239F7-A0B1-459A-AF6F-FB1501544C2B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Novelhall", "src\ExternalServices\Elib2Ebook.ExternalServices.Novelhall\Elib2Ebook.ExternalServices.Novelhall.csproj", "{FB7DB850-11DB-4F7B-9003-13C145442C73}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Novelxo", "src\ExternalServices\Elib2Ebook.ExternalServices.Novelxo\Elib2Ebook.ExternalServices.Novelxo.csproj", "{23BEB12C-DE15-4B88-96F6-5DBDE2E9BA62}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.OnlineKnigi", "src\ExternalServices\Elib2Ebook.ExternalServices.OnlineKnigi\Elib2Ebook.ExternalServices.OnlineKnigi.csproj", "{DF2456D1-0CFF-4213-9E8F-D666DAEA2E54}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Prodaman", "src\ExternalServices\Elib2Ebook.ExternalServices.Prodaman\Elib2Ebook.ExternalServices.Prodaman.csproj", "{1980F468-08B9-4159-B63D-4197EE6FC83B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Ranobe", "src\ExternalServices\Elib2Ebook.ExternalServices.Ranobe\Elib2Ebook.ExternalServices.Ranobe.csproj", "{957FCED2-6B1E-490E-97B4-2B123E565785}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.RanobeHub", "src\ExternalServices\Elib2Ebook.ExternalServices.RanobeHub\Elib2Ebook.ExternalServices.RanobeHub.csproj", "{C67F2919-4530-4EAF-B1A1-CCED621BCCFE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.RanobeOvh", "src\ExternalServices\Elib2Ebook.ExternalServices.RanobeOvh\Elib2Ebook.ExternalServices.RanobeOvh.csproj", "{8CE6E5B6-9BE6-4E8E-B17A-A9CA0148842E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.RanobesCom", "src\ExternalServices\Elib2Ebook.ExternalServices.RanobesCom\Elib2Ebook.ExternalServices.RanobesCom.csproj", "{035C2D5E-18DD-4CFC-98E2-0A8BF69A1511}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.ReadManga", "src\ExternalServices\Elib2Ebook.ExternalServices.ReadManga\Elib2Ebook.ExternalServices.ReadManga.csproj", "{2FAEDBBC-6F75-46AC-A281-F507412EDF6A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Readli", "src\ExternalServices\Elib2Ebook.ExternalServices.Readli\Elib2Ebook.ExternalServices.Readli.csproj", "{F00538BE-2AF8-4388-BF7C-59934838B7BD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Renovels", "src\ExternalServices\Elib2Ebook.ExternalServices.Renovels\Elib2Ebook.ExternalServices.Renovels.csproj", "{06A50D0C-EAF4-4599-83A9-B23C0756ADF5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Romfant", "src\ExternalServices\Elib2Ebook.ExternalServices.Romfant\Elib2Ebook.ExternalServices.Romfant.csproj", "{86DEDD76-E85D-44C9-BDF6-7D3299F0AED7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.RoyalRoad", "src\ExternalServices\Elib2Ebook.ExternalServices.RoyalRoad\Elib2Ebook.ExternalServices.RoyalRoad.csproj", "{994DA72D-7712-4AF4-A1FC-6E53CFD9ACD9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Rulate", "src\ExternalServices\Elib2Ebook.ExternalServices.Rulate\Elib2Ebook.ExternalServices.Rulate.csproj", "{8F625A63-FFCD-4F8F-8C5C-97252728AA8B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Samlib", "src\ExternalServices\Elib2Ebook.ExternalServices.Samlib\Elib2Ebook.ExternalServices.Samlib.csproj", "{BC735304-7981-4854-A9EE-00FD55943BF4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.StrokiMts", "src\ExternalServices\Elib2Ebook.ExternalServices.StrokiMts\Elib2Ebook.ExternalServices.StrokiMts.csproj", "{EF4C2EAF-8CF8-4E43-BBAA-9A2017C43B80}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.TopLiba", "src\ExternalServices\Elib2Ebook.ExternalServices.TopLiba\Elib2Ebook.ExternalServices.TopLiba.csproj", "{E6717F55-79BC-494A-84B1-215491824F20}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.TwilightRussia", "src\ExternalServices\Elib2Ebook.ExternalServices.TwilightRussia\Elib2Ebook.ExternalServices.TwilightRussia.csproj", "{7CA07B2A-F612-47F0-8F4F-E7771BF9DED1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Wattpad", "src\ExternalServices\Elib2Ebook.ExternalServices.Wattpad\Elib2Ebook.ExternalServices.Wattpad.csproj", "{FA121F56-04BB-46A1-B8C1-0C282295584B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.WuxiaWorld", "src\ExternalServices\Elib2Ebook.ExternalServices.WuxiaWorld\Elib2Ebook.ExternalServices.WuxiaWorld.csproj", "{3C0A98D5-DAFF-4FF1-9A59-F7409CB66754}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.ExternalServices.Younettranslate", "src\ExternalServices\Elib2Ebook.ExternalServices.Younettranslate\Elib2Ebook.ExternalServices.Younettranslate.csproj", "{46B6E125-FF47-4453-9172-5DF695687FDF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.Domain", "src\Elib2Ebook.Domain\Elib2Ebook.Domain.csproj", "{07A39741-8656-488A-964C-D8387A1925DF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elib2Ebook.DomainServices", "src\Elib2Ebook.DomainServices\Elib2Ebook.DomainServices.csproj", "{76924193-4339-4BC8-8C0B-CA5F0A5047FB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -28,18 +158,6 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Debug|x64.ActiveCfg = Debug|Any CPU - {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Debug|x64.Build.0 = Debug|Any CPU - {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Debug|x86.ActiveCfg = Debug|Any CPU - {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Debug|x86.Build.0 = Debug|Any CPU - {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Release|Any CPU.Build.0 = Release|Any CPU - {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Release|x64.ActiveCfg = Release|Any CPU - {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Release|x64.Build.0 = Release|Any CPU - {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Release|x86.ActiveCfg = Release|Any CPU - {3576A033-7429-4DE3-BBE3-CD703A4564ED}.Release|x86.Build.0 = Release|Any CPU {C3CCD71A-37D0-4665-A628-AC6185313A4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C3CCD71A-37D0-4665-A628-AC6185313A4C}.Debug|Any CPU.Build.0 = Debug|Any CPU {C3CCD71A-37D0-4665-A628-AC6185313A4C}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -76,14 +194,859 @@ Global {97B6CA29-9704-4A44-8879-E7015403E743}.Release|x64.Build.0 = Release|Any CPU {97B6CA29-9704-4A44-8879-E7015403E743}.Release|x86.ActiveCfg = Release|Any CPU {97B6CA29-9704-4A44-8879-E7015403E743}.Release|x86.Build.0 = Release|Any CPU + {4FBF6A86-DE1B-45E3-A1CE-B1271E927DF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4FBF6A86-DE1B-45E3-A1CE-B1271E927DF8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4FBF6A86-DE1B-45E3-A1CE-B1271E927DF8}.Debug|x64.ActiveCfg = Debug|Any CPU + {4FBF6A86-DE1B-45E3-A1CE-B1271E927DF8}.Debug|x64.Build.0 = Debug|Any CPU + {4FBF6A86-DE1B-45E3-A1CE-B1271E927DF8}.Debug|x86.ActiveCfg = Debug|Any CPU + {4FBF6A86-DE1B-45E3-A1CE-B1271E927DF8}.Debug|x86.Build.0 = Debug|Any CPU + {4FBF6A86-DE1B-45E3-A1CE-B1271E927DF8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4FBF6A86-DE1B-45E3-A1CE-B1271E927DF8}.Release|Any CPU.Build.0 = Release|Any CPU + {4FBF6A86-DE1B-45E3-A1CE-B1271E927DF8}.Release|x64.ActiveCfg = Release|Any CPU + {4FBF6A86-DE1B-45E3-A1CE-B1271E927DF8}.Release|x64.Build.0 = Release|Any CPU + {4FBF6A86-DE1B-45E3-A1CE-B1271E927DF8}.Release|x86.ActiveCfg = Release|Any CPU + {4FBF6A86-DE1B-45E3-A1CE-B1271E927DF8}.Release|x86.Build.0 = Release|Any CPU + {898F86C5-1886-41BE-97DD-46A182261678}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {898F86C5-1886-41BE-97DD-46A182261678}.Debug|Any CPU.Build.0 = Debug|Any CPU + {898F86C5-1886-41BE-97DD-46A182261678}.Debug|x64.ActiveCfg = Debug|Any CPU + {898F86C5-1886-41BE-97DD-46A182261678}.Debug|x64.Build.0 = Debug|Any CPU + {898F86C5-1886-41BE-97DD-46A182261678}.Debug|x86.ActiveCfg = Debug|Any CPU + {898F86C5-1886-41BE-97DD-46A182261678}.Debug|x86.Build.0 = Debug|Any CPU + {898F86C5-1886-41BE-97DD-46A182261678}.Release|Any CPU.ActiveCfg = Release|Any CPU + {898F86C5-1886-41BE-97DD-46A182261678}.Release|Any CPU.Build.0 = Release|Any CPU + {898F86C5-1886-41BE-97DD-46A182261678}.Release|x64.ActiveCfg = Release|Any CPU + {898F86C5-1886-41BE-97DD-46A182261678}.Release|x64.Build.0 = Release|Any CPU + {898F86C5-1886-41BE-97DD-46A182261678}.Release|x86.ActiveCfg = Release|Any CPU + {898F86C5-1886-41BE-97DD-46A182261678}.Release|x86.Build.0 = Release|Any CPU + {88836B48-12C3-4CC7-A73E-0245970589CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {88836B48-12C3-4CC7-A73E-0245970589CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {88836B48-12C3-4CC7-A73E-0245970589CB}.Debug|x64.ActiveCfg = Debug|Any CPU + {88836B48-12C3-4CC7-A73E-0245970589CB}.Debug|x64.Build.0 = Debug|Any CPU + {88836B48-12C3-4CC7-A73E-0245970589CB}.Debug|x86.ActiveCfg = Debug|Any CPU + {88836B48-12C3-4CC7-A73E-0245970589CB}.Debug|x86.Build.0 = Debug|Any CPU + {88836B48-12C3-4CC7-A73E-0245970589CB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {88836B48-12C3-4CC7-A73E-0245970589CB}.Release|Any CPU.Build.0 = Release|Any CPU + {88836B48-12C3-4CC7-A73E-0245970589CB}.Release|x64.ActiveCfg = Release|Any CPU + {88836B48-12C3-4CC7-A73E-0245970589CB}.Release|x64.Build.0 = Release|Any CPU + {88836B48-12C3-4CC7-A73E-0245970589CB}.Release|x86.ActiveCfg = Release|Any CPU + {88836B48-12C3-4CC7-A73E-0245970589CB}.Release|x86.Build.0 = Release|Any CPU + {297D901A-F3D0-4653-B4EB-CBD3D4402612}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {297D901A-F3D0-4653-B4EB-CBD3D4402612}.Debug|Any CPU.Build.0 = Debug|Any CPU + {297D901A-F3D0-4653-B4EB-CBD3D4402612}.Debug|x64.ActiveCfg = Debug|Any CPU + {297D901A-F3D0-4653-B4EB-CBD3D4402612}.Debug|x64.Build.0 = Debug|Any CPU + {297D901A-F3D0-4653-B4EB-CBD3D4402612}.Debug|x86.ActiveCfg = Debug|Any CPU + {297D901A-F3D0-4653-B4EB-CBD3D4402612}.Debug|x86.Build.0 = Debug|Any CPU + {297D901A-F3D0-4653-B4EB-CBD3D4402612}.Release|Any CPU.ActiveCfg = Release|Any CPU + {297D901A-F3D0-4653-B4EB-CBD3D4402612}.Release|Any CPU.Build.0 = Release|Any CPU + {297D901A-F3D0-4653-B4EB-CBD3D4402612}.Release|x64.ActiveCfg = Release|Any CPU + {297D901A-F3D0-4653-B4EB-CBD3D4402612}.Release|x64.Build.0 = Release|Any CPU + {297D901A-F3D0-4653-B4EB-CBD3D4402612}.Release|x86.ActiveCfg = Release|Any CPU + {297D901A-F3D0-4653-B4EB-CBD3D4402612}.Release|x86.Build.0 = Release|Any CPU + {82456D25-7753-4D1C-9ADC-450948B7FAC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {82456D25-7753-4D1C-9ADC-450948B7FAC0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {82456D25-7753-4D1C-9ADC-450948B7FAC0}.Debug|x64.ActiveCfg = Debug|Any CPU + {82456D25-7753-4D1C-9ADC-450948B7FAC0}.Debug|x64.Build.0 = Debug|Any CPU + {82456D25-7753-4D1C-9ADC-450948B7FAC0}.Debug|x86.ActiveCfg = Debug|Any CPU + {82456D25-7753-4D1C-9ADC-450948B7FAC0}.Debug|x86.Build.0 = Debug|Any CPU + {82456D25-7753-4D1C-9ADC-450948B7FAC0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {82456D25-7753-4D1C-9ADC-450948B7FAC0}.Release|Any CPU.Build.0 = Release|Any CPU + {82456D25-7753-4D1C-9ADC-450948B7FAC0}.Release|x64.ActiveCfg = Release|Any CPU + {82456D25-7753-4D1C-9ADC-450948B7FAC0}.Release|x64.Build.0 = Release|Any CPU + {82456D25-7753-4D1C-9ADC-450948B7FAC0}.Release|x86.ActiveCfg = Release|Any CPU + {82456D25-7753-4D1C-9ADC-450948B7FAC0}.Release|x86.Build.0 = Release|Any CPU + {E5408C31-60C9-4A60-89D9-840C8C9E5E2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5408C31-60C9-4A60-89D9-840C8C9E5E2F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5408C31-60C9-4A60-89D9-840C8C9E5E2F}.Debug|x64.ActiveCfg = Debug|Any CPU + {E5408C31-60C9-4A60-89D9-840C8C9E5E2F}.Debug|x64.Build.0 = Debug|Any CPU + {E5408C31-60C9-4A60-89D9-840C8C9E5E2F}.Debug|x86.ActiveCfg = Debug|Any CPU + {E5408C31-60C9-4A60-89D9-840C8C9E5E2F}.Debug|x86.Build.0 = Debug|Any CPU + {E5408C31-60C9-4A60-89D9-840C8C9E5E2F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5408C31-60C9-4A60-89D9-840C8C9E5E2F}.Release|Any CPU.Build.0 = Release|Any CPU + {E5408C31-60C9-4A60-89D9-840C8C9E5E2F}.Release|x64.ActiveCfg = Release|Any CPU + {E5408C31-60C9-4A60-89D9-840C8C9E5E2F}.Release|x64.Build.0 = Release|Any CPU + {E5408C31-60C9-4A60-89D9-840C8C9E5E2F}.Release|x86.ActiveCfg = Release|Any CPU + {E5408C31-60C9-4A60-89D9-840C8C9E5E2F}.Release|x86.Build.0 = Release|Any CPU + {79094B35-C3C6-4332-8A79-6E3D32582059}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {79094B35-C3C6-4332-8A79-6E3D32582059}.Debug|Any CPU.Build.0 = Debug|Any CPU + {79094B35-C3C6-4332-8A79-6E3D32582059}.Debug|x64.ActiveCfg = Debug|Any CPU + {79094B35-C3C6-4332-8A79-6E3D32582059}.Debug|x64.Build.0 = Debug|Any CPU + {79094B35-C3C6-4332-8A79-6E3D32582059}.Debug|x86.ActiveCfg = Debug|Any CPU + {79094B35-C3C6-4332-8A79-6E3D32582059}.Debug|x86.Build.0 = Debug|Any CPU + {79094B35-C3C6-4332-8A79-6E3D32582059}.Release|Any CPU.ActiveCfg = Release|Any CPU + {79094B35-C3C6-4332-8A79-6E3D32582059}.Release|Any CPU.Build.0 = Release|Any CPU + {79094B35-C3C6-4332-8A79-6E3D32582059}.Release|x64.ActiveCfg = Release|Any CPU + {79094B35-C3C6-4332-8A79-6E3D32582059}.Release|x64.Build.0 = Release|Any CPU + {79094B35-C3C6-4332-8A79-6E3D32582059}.Release|x86.ActiveCfg = Release|Any CPU + {79094B35-C3C6-4332-8A79-6E3D32582059}.Release|x86.Build.0 = Release|Any CPU + {7311835B-6CE8-470E-AFB4-B76424A86096}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7311835B-6CE8-470E-AFB4-B76424A86096}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7311835B-6CE8-470E-AFB4-B76424A86096}.Debug|x64.ActiveCfg = Debug|Any CPU + {7311835B-6CE8-470E-AFB4-B76424A86096}.Debug|x64.Build.0 = Debug|Any CPU + {7311835B-6CE8-470E-AFB4-B76424A86096}.Debug|x86.ActiveCfg = Debug|Any CPU + {7311835B-6CE8-470E-AFB4-B76424A86096}.Debug|x86.Build.0 = Debug|Any CPU + {7311835B-6CE8-470E-AFB4-B76424A86096}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7311835B-6CE8-470E-AFB4-B76424A86096}.Release|Any CPU.Build.0 = Release|Any CPU + {7311835B-6CE8-470E-AFB4-B76424A86096}.Release|x64.ActiveCfg = Release|Any CPU + {7311835B-6CE8-470E-AFB4-B76424A86096}.Release|x64.Build.0 = Release|Any CPU + {7311835B-6CE8-470E-AFB4-B76424A86096}.Release|x86.ActiveCfg = Release|Any CPU + {7311835B-6CE8-470E-AFB4-B76424A86096}.Release|x86.Build.0 = Release|Any CPU + {04F673E8-6A3D-4A60-A829-1B851FDB5A4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04F673E8-6A3D-4A60-A829-1B851FDB5A4C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04F673E8-6A3D-4A60-A829-1B851FDB5A4C}.Debug|x64.ActiveCfg = Debug|Any CPU + {04F673E8-6A3D-4A60-A829-1B851FDB5A4C}.Debug|x64.Build.0 = Debug|Any CPU + {04F673E8-6A3D-4A60-A829-1B851FDB5A4C}.Debug|x86.ActiveCfg = Debug|Any CPU + {04F673E8-6A3D-4A60-A829-1B851FDB5A4C}.Debug|x86.Build.0 = Debug|Any CPU + {04F673E8-6A3D-4A60-A829-1B851FDB5A4C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04F673E8-6A3D-4A60-A829-1B851FDB5A4C}.Release|Any CPU.Build.0 = Release|Any CPU + {04F673E8-6A3D-4A60-A829-1B851FDB5A4C}.Release|x64.ActiveCfg = Release|Any CPU + {04F673E8-6A3D-4A60-A829-1B851FDB5A4C}.Release|x64.Build.0 = Release|Any CPU + {04F673E8-6A3D-4A60-A829-1B851FDB5A4C}.Release|x86.ActiveCfg = Release|Any CPU + {04F673E8-6A3D-4A60-A829-1B851FDB5A4C}.Release|x86.Build.0 = Release|Any CPU + {1826ECD1-D177-4AEF-B821-8B65535CAA46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1826ECD1-D177-4AEF-B821-8B65535CAA46}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1826ECD1-D177-4AEF-B821-8B65535CAA46}.Debug|x64.ActiveCfg = Debug|Any CPU + {1826ECD1-D177-4AEF-B821-8B65535CAA46}.Debug|x64.Build.0 = Debug|Any CPU + {1826ECD1-D177-4AEF-B821-8B65535CAA46}.Debug|x86.ActiveCfg = Debug|Any CPU + {1826ECD1-D177-4AEF-B821-8B65535CAA46}.Debug|x86.Build.0 = Debug|Any CPU + {1826ECD1-D177-4AEF-B821-8B65535CAA46}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1826ECD1-D177-4AEF-B821-8B65535CAA46}.Release|Any CPU.Build.0 = Release|Any CPU + {1826ECD1-D177-4AEF-B821-8B65535CAA46}.Release|x64.ActiveCfg = Release|Any CPU + {1826ECD1-D177-4AEF-B821-8B65535CAA46}.Release|x64.Build.0 = Release|Any CPU + {1826ECD1-D177-4AEF-B821-8B65535CAA46}.Release|x86.ActiveCfg = Release|Any CPU + {1826ECD1-D177-4AEF-B821-8B65535CAA46}.Release|x86.Build.0 = Release|Any CPU + {C6D9607E-AB00-4E29-A8A9-8F378B2D755B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C6D9607E-AB00-4E29-A8A9-8F378B2D755B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C6D9607E-AB00-4E29-A8A9-8F378B2D755B}.Debug|x64.ActiveCfg = Debug|Any CPU + {C6D9607E-AB00-4E29-A8A9-8F378B2D755B}.Debug|x64.Build.0 = Debug|Any CPU + {C6D9607E-AB00-4E29-A8A9-8F378B2D755B}.Debug|x86.ActiveCfg = Debug|Any CPU + {C6D9607E-AB00-4E29-A8A9-8F378B2D755B}.Debug|x86.Build.0 = Debug|Any CPU + {C6D9607E-AB00-4E29-A8A9-8F378B2D755B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C6D9607E-AB00-4E29-A8A9-8F378B2D755B}.Release|Any CPU.Build.0 = Release|Any CPU + {C6D9607E-AB00-4E29-A8A9-8F378B2D755B}.Release|x64.ActiveCfg = Release|Any CPU + {C6D9607E-AB00-4E29-A8A9-8F378B2D755B}.Release|x64.Build.0 = Release|Any CPU + {C6D9607E-AB00-4E29-A8A9-8F378B2D755B}.Release|x86.ActiveCfg = Release|Any CPU + {C6D9607E-AB00-4E29-A8A9-8F378B2D755B}.Release|x86.Build.0 = Release|Any CPU + {19462AF9-35D7-43C8-AFA1-39E2FD1C7EBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {19462AF9-35D7-43C8-AFA1-39E2FD1C7EBD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {19462AF9-35D7-43C8-AFA1-39E2FD1C7EBD}.Debug|x64.ActiveCfg = Debug|Any CPU + {19462AF9-35D7-43C8-AFA1-39E2FD1C7EBD}.Debug|x64.Build.0 = Debug|Any CPU + {19462AF9-35D7-43C8-AFA1-39E2FD1C7EBD}.Debug|x86.ActiveCfg = Debug|Any CPU + {19462AF9-35D7-43C8-AFA1-39E2FD1C7EBD}.Debug|x86.Build.0 = Debug|Any CPU + {19462AF9-35D7-43C8-AFA1-39E2FD1C7EBD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {19462AF9-35D7-43C8-AFA1-39E2FD1C7EBD}.Release|Any CPU.Build.0 = Release|Any CPU + {19462AF9-35D7-43C8-AFA1-39E2FD1C7EBD}.Release|x64.ActiveCfg = Release|Any CPU + {19462AF9-35D7-43C8-AFA1-39E2FD1C7EBD}.Release|x64.Build.0 = Release|Any CPU + {19462AF9-35D7-43C8-AFA1-39E2FD1C7EBD}.Release|x86.ActiveCfg = Release|Any CPU + {19462AF9-35D7-43C8-AFA1-39E2FD1C7EBD}.Release|x86.Build.0 = Release|Any CPU + {745684DE-BABB-4BAA-BC36-DA3029E8C99F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {745684DE-BABB-4BAA-BC36-DA3029E8C99F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {745684DE-BABB-4BAA-BC36-DA3029E8C99F}.Debug|x64.ActiveCfg = Debug|Any CPU + {745684DE-BABB-4BAA-BC36-DA3029E8C99F}.Debug|x64.Build.0 = Debug|Any CPU + {745684DE-BABB-4BAA-BC36-DA3029E8C99F}.Debug|x86.ActiveCfg = Debug|Any CPU + {745684DE-BABB-4BAA-BC36-DA3029E8C99F}.Debug|x86.Build.0 = Debug|Any CPU + {745684DE-BABB-4BAA-BC36-DA3029E8C99F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {745684DE-BABB-4BAA-BC36-DA3029E8C99F}.Release|Any CPU.Build.0 = Release|Any CPU + {745684DE-BABB-4BAA-BC36-DA3029E8C99F}.Release|x64.ActiveCfg = Release|Any CPU + {745684DE-BABB-4BAA-BC36-DA3029E8C99F}.Release|x64.Build.0 = Release|Any CPU + {745684DE-BABB-4BAA-BC36-DA3029E8C99F}.Release|x86.ActiveCfg = Release|Any CPU + {745684DE-BABB-4BAA-BC36-DA3029E8C99F}.Release|x86.Build.0 = Release|Any CPU + {F58C0BE6-D91A-42F7-A221-4A882911E61C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F58C0BE6-D91A-42F7-A221-4A882911E61C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F58C0BE6-D91A-42F7-A221-4A882911E61C}.Debug|x64.ActiveCfg = Debug|Any CPU + {F58C0BE6-D91A-42F7-A221-4A882911E61C}.Debug|x64.Build.0 = Debug|Any CPU + {F58C0BE6-D91A-42F7-A221-4A882911E61C}.Debug|x86.ActiveCfg = Debug|Any CPU + {F58C0BE6-D91A-42F7-A221-4A882911E61C}.Debug|x86.Build.0 = Debug|Any CPU + {F58C0BE6-D91A-42F7-A221-4A882911E61C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F58C0BE6-D91A-42F7-A221-4A882911E61C}.Release|Any CPU.Build.0 = Release|Any CPU + {F58C0BE6-D91A-42F7-A221-4A882911E61C}.Release|x64.ActiveCfg = Release|Any CPU + {F58C0BE6-D91A-42F7-A221-4A882911E61C}.Release|x64.Build.0 = Release|Any CPU + {F58C0BE6-D91A-42F7-A221-4A882911E61C}.Release|x86.ActiveCfg = Release|Any CPU + {F58C0BE6-D91A-42F7-A221-4A882911E61C}.Release|x86.Build.0 = Release|Any CPU + {5F1D96A7-8252-4D60-AE6E-A66C045A8A92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F1D96A7-8252-4D60-AE6E-A66C045A8A92}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F1D96A7-8252-4D60-AE6E-A66C045A8A92}.Debug|x64.ActiveCfg = Debug|Any CPU + {5F1D96A7-8252-4D60-AE6E-A66C045A8A92}.Debug|x64.Build.0 = Debug|Any CPU + {5F1D96A7-8252-4D60-AE6E-A66C045A8A92}.Debug|x86.ActiveCfg = Debug|Any CPU + {5F1D96A7-8252-4D60-AE6E-A66C045A8A92}.Debug|x86.Build.0 = Debug|Any CPU + {5F1D96A7-8252-4D60-AE6E-A66C045A8A92}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F1D96A7-8252-4D60-AE6E-A66C045A8A92}.Release|Any CPU.Build.0 = Release|Any CPU + {5F1D96A7-8252-4D60-AE6E-A66C045A8A92}.Release|x64.ActiveCfg = Release|Any CPU + {5F1D96A7-8252-4D60-AE6E-A66C045A8A92}.Release|x64.Build.0 = Release|Any CPU + {5F1D96A7-8252-4D60-AE6E-A66C045A8A92}.Release|x86.ActiveCfg = Release|Any CPU + {5F1D96A7-8252-4D60-AE6E-A66C045A8A92}.Release|x86.Build.0 = Release|Any CPU + {AEDD4AE8-A831-46EE-AF49-EAC3183D5971}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AEDD4AE8-A831-46EE-AF49-EAC3183D5971}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AEDD4AE8-A831-46EE-AF49-EAC3183D5971}.Debug|x64.ActiveCfg = Debug|Any CPU + {AEDD4AE8-A831-46EE-AF49-EAC3183D5971}.Debug|x64.Build.0 = Debug|Any CPU + {AEDD4AE8-A831-46EE-AF49-EAC3183D5971}.Debug|x86.ActiveCfg = Debug|Any CPU + {AEDD4AE8-A831-46EE-AF49-EAC3183D5971}.Debug|x86.Build.0 = Debug|Any CPU + {AEDD4AE8-A831-46EE-AF49-EAC3183D5971}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AEDD4AE8-A831-46EE-AF49-EAC3183D5971}.Release|Any CPU.Build.0 = Release|Any CPU + {AEDD4AE8-A831-46EE-AF49-EAC3183D5971}.Release|x64.ActiveCfg = Release|Any CPU + {AEDD4AE8-A831-46EE-AF49-EAC3183D5971}.Release|x64.Build.0 = Release|Any CPU + {AEDD4AE8-A831-46EE-AF49-EAC3183D5971}.Release|x86.ActiveCfg = Release|Any CPU + {AEDD4AE8-A831-46EE-AF49-EAC3183D5971}.Release|x86.Build.0 = Release|Any CPU + {F2387E3F-8B78-4FAC-BDAA-A3E2EBC4D889}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2387E3F-8B78-4FAC-BDAA-A3E2EBC4D889}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2387E3F-8B78-4FAC-BDAA-A3E2EBC4D889}.Debug|x64.ActiveCfg = Debug|Any CPU + {F2387E3F-8B78-4FAC-BDAA-A3E2EBC4D889}.Debug|x64.Build.0 = Debug|Any CPU + {F2387E3F-8B78-4FAC-BDAA-A3E2EBC4D889}.Debug|x86.ActiveCfg = Debug|Any CPU + {F2387E3F-8B78-4FAC-BDAA-A3E2EBC4D889}.Debug|x86.Build.0 = Debug|Any CPU + {F2387E3F-8B78-4FAC-BDAA-A3E2EBC4D889}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F2387E3F-8B78-4FAC-BDAA-A3E2EBC4D889}.Release|Any CPU.Build.0 = Release|Any CPU + {F2387E3F-8B78-4FAC-BDAA-A3E2EBC4D889}.Release|x64.ActiveCfg = Release|Any CPU + {F2387E3F-8B78-4FAC-BDAA-A3E2EBC4D889}.Release|x64.Build.0 = Release|Any CPU + {F2387E3F-8B78-4FAC-BDAA-A3E2EBC4D889}.Release|x86.ActiveCfg = Release|Any CPU + {F2387E3F-8B78-4FAC-BDAA-A3E2EBC4D889}.Release|x86.Build.0 = Release|Any CPU + {D509243A-E5AA-430D-B47E-8ADC5DA31125}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D509243A-E5AA-430D-B47E-8ADC5DA31125}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D509243A-E5AA-430D-B47E-8ADC5DA31125}.Debug|x64.ActiveCfg = Debug|Any CPU + {D509243A-E5AA-430D-B47E-8ADC5DA31125}.Debug|x64.Build.0 = Debug|Any CPU + {D509243A-E5AA-430D-B47E-8ADC5DA31125}.Debug|x86.ActiveCfg = Debug|Any CPU + {D509243A-E5AA-430D-B47E-8ADC5DA31125}.Debug|x86.Build.0 = Debug|Any CPU + {D509243A-E5AA-430D-B47E-8ADC5DA31125}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D509243A-E5AA-430D-B47E-8ADC5DA31125}.Release|Any CPU.Build.0 = Release|Any CPU + {D509243A-E5AA-430D-B47E-8ADC5DA31125}.Release|x64.ActiveCfg = Release|Any CPU + {D509243A-E5AA-430D-B47E-8ADC5DA31125}.Release|x64.Build.0 = Release|Any CPU + {D509243A-E5AA-430D-B47E-8ADC5DA31125}.Release|x86.ActiveCfg = Release|Any CPU + {D509243A-E5AA-430D-B47E-8ADC5DA31125}.Release|x86.Build.0 = Release|Any CPU + {7C8AA7A4-E72E-4A21-BC13-D7BA5A2219DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C8AA7A4-E72E-4A21-BC13-D7BA5A2219DC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C8AA7A4-E72E-4A21-BC13-D7BA5A2219DC}.Debug|x64.ActiveCfg = Debug|Any CPU + {7C8AA7A4-E72E-4A21-BC13-D7BA5A2219DC}.Debug|x64.Build.0 = Debug|Any CPU + {7C8AA7A4-E72E-4A21-BC13-D7BA5A2219DC}.Debug|x86.ActiveCfg = Debug|Any CPU + {7C8AA7A4-E72E-4A21-BC13-D7BA5A2219DC}.Debug|x86.Build.0 = Debug|Any CPU + {7C8AA7A4-E72E-4A21-BC13-D7BA5A2219DC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C8AA7A4-E72E-4A21-BC13-D7BA5A2219DC}.Release|Any CPU.Build.0 = Release|Any CPU + {7C8AA7A4-E72E-4A21-BC13-D7BA5A2219DC}.Release|x64.ActiveCfg = Release|Any CPU + {7C8AA7A4-E72E-4A21-BC13-D7BA5A2219DC}.Release|x64.Build.0 = Release|Any CPU + {7C8AA7A4-E72E-4A21-BC13-D7BA5A2219DC}.Release|x86.ActiveCfg = Release|Any CPU + {7C8AA7A4-E72E-4A21-BC13-D7BA5A2219DC}.Release|x86.Build.0 = Release|Any CPU + {443B5C89-0904-4AE8-B48F-1E92709FBEF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {443B5C89-0904-4AE8-B48F-1E92709FBEF3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {443B5C89-0904-4AE8-B48F-1E92709FBEF3}.Debug|x64.ActiveCfg = Debug|Any CPU + {443B5C89-0904-4AE8-B48F-1E92709FBEF3}.Debug|x64.Build.0 = Debug|Any CPU + {443B5C89-0904-4AE8-B48F-1E92709FBEF3}.Debug|x86.ActiveCfg = Debug|Any CPU + {443B5C89-0904-4AE8-B48F-1E92709FBEF3}.Debug|x86.Build.0 = Debug|Any CPU + {443B5C89-0904-4AE8-B48F-1E92709FBEF3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {443B5C89-0904-4AE8-B48F-1E92709FBEF3}.Release|Any CPU.Build.0 = Release|Any CPU + {443B5C89-0904-4AE8-B48F-1E92709FBEF3}.Release|x64.ActiveCfg = Release|Any CPU + {443B5C89-0904-4AE8-B48F-1E92709FBEF3}.Release|x64.Build.0 = Release|Any CPU + {443B5C89-0904-4AE8-B48F-1E92709FBEF3}.Release|x86.ActiveCfg = Release|Any CPU + {443B5C89-0904-4AE8-B48F-1E92709FBEF3}.Release|x86.Build.0 = Release|Any CPU + {48EA13C9-7FBC-4DCA-85CF-CCB1493A73C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {48EA13C9-7FBC-4DCA-85CF-CCB1493A73C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48EA13C9-7FBC-4DCA-85CF-CCB1493A73C8}.Debug|x64.ActiveCfg = Debug|Any CPU + {48EA13C9-7FBC-4DCA-85CF-CCB1493A73C8}.Debug|x64.Build.0 = Debug|Any CPU + {48EA13C9-7FBC-4DCA-85CF-CCB1493A73C8}.Debug|x86.ActiveCfg = Debug|Any CPU + {48EA13C9-7FBC-4DCA-85CF-CCB1493A73C8}.Debug|x86.Build.0 = Debug|Any CPU + {48EA13C9-7FBC-4DCA-85CF-CCB1493A73C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48EA13C9-7FBC-4DCA-85CF-CCB1493A73C8}.Release|Any CPU.Build.0 = Release|Any CPU + {48EA13C9-7FBC-4DCA-85CF-CCB1493A73C8}.Release|x64.ActiveCfg = Release|Any CPU + {48EA13C9-7FBC-4DCA-85CF-CCB1493A73C8}.Release|x64.Build.0 = Release|Any CPU + {48EA13C9-7FBC-4DCA-85CF-CCB1493A73C8}.Release|x86.ActiveCfg = Release|Any CPU + {48EA13C9-7FBC-4DCA-85CF-CCB1493A73C8}.Release|x86.Build.0 = Release|Any CPU + {EF1817C6-1FEB-4185-94F3-6BDB9ED1660A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF1817C6-1FEB-4185-94F3-6BDB9ED1660A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF1817C6-1FEB-4185-94F3-6BDB9ED1660A}.Debug|x64.ActiveCfg = Debug|Any CPU + {EF1817C6-1FEB-4185-94F3-6BDB9ED1660A}.Debug|x64.Build.0 = Debug|Any CPU + {EF1817C6-1FEB-4185-94F3-6BDB9ED1660A}.Debug|x86.ActiveCfg = Debug|Any CPU + {EF1817C6-1FEB-4185-94F3-6BDB9ED1660A}.Debug|x86.Build.0 = Debug|Any CPU + {EF1817C6-1FEB-4185-94F3-6BDB9ED1660A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF1817C6-1FEB-4185-94F3-6BDB9ED1660A}.Release|Any CPU.Build.0 = Release|Any CPU + {EF1817C6-1FEB-4185-94F3-6BDB9ED1660A}.Release|x64.ActiveCfg = Release|Any CPU + {EF1817C6-1FEB-4185-94F3-6BDB9ED1660A}.Release|x64.Build.0 = Release|Any CPU + {EF1817C6-1FEB-4185-94F3-6BDB9ED1660A}.Release|x86.ActiveCfg = Release|Any CPU + {EF1817C6-1FEB-4185-94F3-6BDB9ED1660A}.Release|x86.Build.0 = Release|Any CPU + {1EF56286-3151-4D13-B662-C23CBE7148FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1EF56286-3151-4D13-B662-C23CBE7148FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1EF56286-3151-4D13-B662-C23CBE7148FE}.Debug|x64.ActiveCfg = Debug|Any CPU + {1EF56286-3151-4D13-B662-C23CBE7148FE}.Debug|x64.Build.0 = Debug|Any CPU + {1EF56286-3151-4D13-B662-C23CBE7148FE}.Debug|x86.ActiveCfg = Debug|Any CPU + {1EF56286-3151-4D13-B662-C23CBE7148FE}.Debug|x86.Build.0 = Debug|Any CPU + {1EF56286-3151-4D13-B662-C23CBE7148FE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1EF56286-3151-4D13-B662-C23CBE7148FE}.Release|Any CPU.Build.0 = Release|Any CPU + {1EF56286-3151-4D13-B662-C23CBE7148FE}.Release|x64.ActiveCfg = Release|Any CPU + {1EF56286-3151-4D13-B662-C23CBE7148FE}.Release|x64.Build.0 = Release|Any CPU + {1EF56286-3151-4D13-B662-C23CBE7148FE}.Release|x86.ActiveCfg = Release|Any CPU + {1EF56286-3151-4D13-B662-C23CBE7148FE}.Release|x86.Build.0 = Release|Any CPU + {5FF3DBB0-B465-46C5-ADB7-CE8998A6E7BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5FF3DBB0-B465-46C5-ADB7-CE8998A6E7BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5FF3DBB0-B465-46C5-ADB7-CE8998A6E7BA}.Debug|x64.ActiveCfg = Debug|Any CPU + {5FF3DBB0-B465-46C5-ADB7-CE8998A6E7BA}.Debug|x64.Build.0 = Debug|Any CPU + {5FF3DBB0-B465-46C5-ADB7-CE8998A6E7BA}.Debug|x86.ActiveCfg = Debug|Any CPU + {5FF3DBB0-B465-46C5-ADB7-CE8998A6E7BA}.Debug|x86.Build.0 = Debug|Any CPU + {5FF3DBB0-B465-46C5-ADB7-CE8998A6E7BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5FF3DBB0-B465-46C5-ADB7-CE8998A6E7BA}.Release|Any CPU.Build.0 = Release|Any CPU + {5FF3DBB0-B465-46C5-ADB7-CE8998A6E7BA}.Release|x64.ActiveCfg = Release|Any CPU + {5FF3DBB0-B465-46C5-ADB7-CE8998A6E7BA}.Release|x64.Build.0 = Release|Any CPU + {5FF3DBB0-B465-46C5-ADB7-CE8998A6E7BA}.Release|x86.ActiveCfg = Release|Any CPU + {5FF3DBB0-B465-46C5-ADB7-CE8998A6E7BA}.Release|x86.Build.0 = Release|Any CPU + {F743054C-0D7F-4352-9DBD-365C4CFA19C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F743054C-0D7F-4352-9DBD-365C4CFA19C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F743054C-0D7F-4352-9DBD-365C4CFA19C6}.Debug|x64.ActiveCfg = Debug|Any CPU + {F743054C-0D7F-4352-9DBD-365C4CFA19C6}.Debug|x64.Build.0 = Debug|Any CPU + {F743054C-0D7F-4352-9DBD-365C4CFA19C6}.Debug|x86.ActiveCfg = Debug|Any CPU + {F743054C-0D7F-4352-9DBD-365C4CFA19C6}.Debug|x86.Build.0 = Debug|Any CPU + {F743054C-0D7F-4352-9DBD-365C4CFA19C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F743054C-0D7F-4352-9DBD-365C4CFA19C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F743054C-0D7F-4352-9DBD-365C4CFA19C6}.Release|x64.ActiveCfg = Release|Any CPU + {F743054C-0D7F-4352-9DBD-365C4CFA19C6}.Release|x64.Build.0 = Release|Any CPU + {F743054C-0D7F-4352-9DBD-365C4CFA19C6}.Release|x86.ActiveCfg = Release|Any CPU + {F743054C-0D7F-4352-9DBD-365C4CFA19C6}.Release|x86.Build.0 = Release|Any CPU + {509EA8AB-5717-4953-87FC-F3EDD041F200}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {509EA8AB-5717-4953-87FC-F3EDD041F200}.Debug|Any CPU.Build.0 = Debug|Any CPU + {509EA8AB-5717-4953-87FC-F3EDD041F200}.Debug|x64.ActiveCfg = Debug|Any CPU + {509EA8AB-5717-4953-87FC-F3EDD041F200}.Debug|x64.Build.0 = Debug|Any CPU + {509EA8AB-5717-4953-87FC-F3EDD041F200}.Debug|x86.ActiveCfg = Debug|Any CPU + {509EA8AB-5717-4953-87FC-F3EDD041F200}.Debug|x86.Build.0 = Debug|Any CPU + {509EA8AB-5717-4953-87FC-F3EDD041F200}.Release|Any CPU.ActiveCfg = Release|Any CPU + {509EA8AB-5717-4953-87FC-F3EDD041F200}.Release|Any CPU.Build.0 = Release|Any CPU + {509EA8AB-5717-4953-87FC-F3EDD041F200}.Release|x64.ActiveCfg = Release|Any CPU + {509EA8AB-5717-4953-87FC-F3EDD041F200}.Release|x64.Build.0 = Release|Any CPU + {509EA8AB-5717-4953-87FC-F3EDD041F200}.Release|x86.ActiveCfg = Release|Any CPU + {509EA8AB-5717-4953-87FC-F3EDD041F200}.Release|x86.Build.0 = Release|Any CPU + {A9677606-014A-43A4-83D1-7871D1547862}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9677606-014A-43A4-83D1-7871D1547862}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9677606-014A-43A4-83D1-7871D1547862}.Debug|x64.ActiveCfg = Debug|Any CPU + {A9677606-014A-43A4-83D1-7871D1547862}.Debug|x64.Build.0 = Debug|Any CPU + {A9677606-014A-43A4-83D1-7871D1547862}.Debug|x86.ActiveCfg = Debug|Any CPU + {A9677606-014A-43A4-83D1-7871D1547862}.Debug|x86.Build.0 = Debug|Any CPU + {A9677606-014A-43A4-83D1-7871D1547862}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9677606-014A-43A4-83D1-7871D1547862}.Release|Any CPU.Build.0 = Release|Any CPU + {A9677606-014A-43A4-83D1-7871D1547862}.Release|x64.ActiveCfg = Release|Any CPU + {A9677606-014A-43A4-83D1-7871D1547862}.Release|x64.Build.0 = Release|Any CPU + {A9677606-014A-43A4-83D1-7871D1547862}.Release|x86.ActiveCfg = Release|Any CPU + {A9677606-014A-43A4-83D1-7871D1547862}.Release|x86.Build.0 = Release|Any CPU + {42B441C3-43AD-4D8F-96E0-547D7257FB04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42B441C3-43AD-4D8F-96E0-547D7257FB04}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42B441C3-43AD-4D8F-96E0-547D7257FB04}.Debug|x64.ActiveCfg = Debug|Any CPU + {42B441C3-43AD-4D8F-96E0-547D7257FB04}.Debug|x64.Build.0 = Debug|Any CPU + {42B441C3-43AD-4D8F-96E0-547D7257FB04}.Debug|x86.ActiveCfg = Debug|Any CPU + {42B441C3-43AD-4D8F-96E0-547D7257FB04}.Debug|x86.Build.0 = Debug|Any CPU + {42B441C3-43AD-4D8F-96E0-547D7257FB04}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42B441C3-43AD-4D8F-96E0-547D7257FB04}.Release|Any CPU.Build.0 = Release|Any CPU + {42B441C3-43AD-4D8F-96E0-547D7257FB04}.Release|x64.ActiveCfg = Release|Any CPU + {42B441C3-43AD-4D8F-96E0-547D7257FB04}.Release|x64.Build.0 = Release|Any CPU + {42B441C3-43AD-4D8F-96E0-547D7257FB04}.Release|x86.ActiveCfg = Release|Any CPU + {42B441C3-43AD-4D8F-96E0-547D7257FB04}.Release|x86.Build.0 = Release|Any CPU + {DD4CFE16-3F58-4AA0-A995-312AFC55F0B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DD4CFE16-3F58-4AA0-A995-312AFC55F0B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DD4CFE16-3F58-4AA0-A995-312AFC55F0B3}.Debug|x64.ActiveCfg = Debug|Any CPU + {DD4CFE16-3F58-4AA0-A995-312AFC55F0B3}.Debug|x64.Build.0 = Debug|Any CPU + {DD4CFE16-3F58-4AA0-A995-312AFC55F0B3}.Debug|x86.ActiveCfg = Debug|Any CPU + {DD4CFE16-3F58-4AA0-A995-312AFC55F0B3}.Debug|x86.Build.0 = Debug|Any CPU + {DD4CFE16-3F58-4AA0-A995-312AFC55F0B3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DD4CFE16-3F58-4AA0-A995-312AFC55F0B3}.Release|Any CPU.Build.0 = Release|Any CPU + {DD4CFE16-3F58-4AA0-A995-312AFC55F0B3}.Release|x64.ActiveCfg = Release|Any CPU + {DD4CFE16-3F58-4AA0-A995-312AFC55F0B3}.Release|x64.Build.0 = Release|Any CPU + {DD4CFE16-3F58-4AA0-A995-312AFC55F0B3}.Release|x86.ActiveCfg = Release|Any CPU + {DD4CFE16-3F58-4AA0-A995-312AFC55F0B3}.Release|x86.Build.0 = Release|Any CPU + {CC31B699-7B15-4685-8E00-01E2DE6CA88B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC31B699-7B15-4685-8E00-01E2DE6CA88B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC31B699-7B15-4685-8E00-01E2DE6CA88B}.Debug|x64.ActiveCfg = Debug|Any CPU + {CC31B699-7B15-4685-8E00-01E2DE6CA88B}.Debug|x64.Build.0 = Debug|Any CPU + {CC31B699-7B15-4685-8E00-01E2DE6CA88B}.Debug|x86.ActiveCfg = Debug|Any CPU + {CC31B699-7B15-4685-8E00-01E2DE6CA88B}.Debug|x86.Build.0 = Debug|Any CPU + {CC31B699-7B15-4685-8E00-01E2DE6CA88B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC31B699-7B15-4685-8E00-01E2DE6CA88B}.Release|Any CPU.Build.0 = Release|Any CPU + {CC31B699-7B15-4685-8E00-01E2DE6CA88B}.Release|x64.ActiveCfg = Release|Any CPU + {CC31B699-7B15-4685-8E00-01E2DE6CA88B}.Release|x64.Build.0 = Release|Any CPU + {CC31B699-7B15-4685-8E00-01E2DE6CA88B}.Release|x86.ActiveCfg = Release|Any CPU + {CC31B699-7B15-4685-8E00-01E2DE6CA88B}.Release|x86.Build.0 = Release|Any CPU + {ED59B9E2-82EC-4984-9A27-E9DFF3B13B60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED59B9E2-82EC-4984-9A27-E9DFF3B13B60}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED59B9E2-82EC-4984-9A27-E9DFF3B13B60}.Debug|x64.ActiveCfg = Debug|Any CPU + {ED59B9E2-82EC-4984-9A27-E9DFF3B13B60}.Debug|x64.Build.0 = Debug|Any CPU + {ED59B9E2-82EC-4984-9A27-E9DFF3B13B60}.Debug|x86.ActiveCfg = Debug|Any CPU + {ED59B9E2-82EC-4984-9A27-E9DFF3B13B60}.Debug|x86.Build.0 = Debug|Any CPU + {ED59B9E2-82EC-4984-9A27-E9DFF3B13B60}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED59B9E2-82EC-4984-9A27-E9DFF3B13B60}.Release|Any CPU.Build.0 = Release|Any CPU + {ED59B9E2-82EC-4984-9A27-E9DFF3B13B60}.Release|x64.ActiveCfg = Release|Any CPU + {ED59B9E2-82EC-4984-9A27-E9DFF3B13B60}.Release|x64.Build.0 = Release|Any CPU + {ED59B9E2-82EC-4984-9A27-E9DFF3B13B60}.Release|x86.ActiveCfg = Release|Any CPU + {ED59B9E2-82EC-4984-9A27-E9DFF3B13B60}.Release|x86.Build.0 = Release|Any CPU + {6FC8F397-8F3D-4300-A8B3-6F76B68C959B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6FC8F397-8F3D-4300-A8B3-6F76B68C959B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6FC8F397-8F3D-4300-A8B3-6F76B68C959B}.Debug|x64.ActiveCfg = Debug|Any CPU + {6FC8F397-8F3D-4300-A8B3-6F76B68C959B}.Debug|x64.Build.0 = Debug|Any CPU + {6FC8F397-8F3D-4300-A8B3-6F76B68C959B}.Debug|x86.ActiveCfg = Debug|Any CPU + {6FC8F397-8F3D-4300-A8B3-6F76B68C959B}.Debug|x86.Build.0 = Debug|Any CPU + {6FC8F397-8F3D-4300-A8B3-6F76B68C959B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6FC8F397-8F3D-4300-A8B3-6F76B68C959B}.Release|Any CPU.Build.0 = Release|Any CPU + {6FC8F397-8F3D-4300-A8B3-6F76B68C959B}.Release|x64.ActiveCfg = Release|Any CPU + {6FC8F397-8F3D-4300-A8B3-6F76B68C959B}.Release|x64.Build.0 = Release|Any CPU + {6FC8F397-8F3D-4300-A8B3-6F76B68C959B}.Release|x86.ActiveCfg = Release|Any CPU + {6FC8F397-8F3D-4300-A8B3-6F76B68C959B}.Release|x86.Build.0 = Release|Any CPU + {7B9E64D1-70C9-4462-BE0B-A965A2825919}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7B9E64D1-70C9-4462-BE0B-A965A2825919}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7B9E64D1-70C9-4462-BE0B-A965A2825919}.Debug|x64.ActiveCfg = Debug|Any CPU + {7B9E64D1-70C9-4462-BE0B-A965A2825919}.Debug|x64.Build.0 = Debug|Any CPU + {7B9E64D1-70C9-4462-BE0B-A965A2825919}.Debug|x86.ActiveCfg = Debug|Any CPU + {7B9E64D1-70C9-4462-BE0B-A965A2825919}.Debug|x86.Build.0 = Debug|Any CPU + {7B9E64D1-70C9-4462-BE0B-A965A2825919}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7B9E64D1-70C9-4462-BE0B-A965A2825919}.Release|Any CPU.Build.0 = Release|Any CPU + {7B9E64D1-70C9-4462-BE0B-A965A2825919}.Release|x64.ActiveCfg = Release|Any CPU + {7B9E64D1-70C9-4462-BE0B-A965A2825919}.Release|x64.Build.0 = Release|Any CPU + {7B9E64D1-70C9-4462-BE0B-A965A2825919}.Release|x86.ActiveCfg = Release|Any CPU + {7B9E64D1-70C9-4462-BE0B-A965A2825919}.Release|x86.Build.0 = Release|Any CPU + {82D6EBAF-B0B6-4744-A556-E5667DF823B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {82D6EBAF-B0B6-4744-A556-E5667DF823B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {82D6EBAF-B0B6-4744-A556-E5667DF823B7}.Debug|x64.ActiveCfg = Debug|Any CPU + {82D6EBAF-B0B6-4744-A556-E5667DF823B7}.Debug|x64.Build.0 = Debug|Any CPU + {82D6EBAF-B0B6-4744-A556-E5667DF823B7}.Debug|x86.ActiveCfg = Debug|Any CPU + {82D6EBAF-B0B6-4744-A556-E5667DF823B7}.Debug|x86.Build.0 = Debug|Any CPU + {82D6EBAF-B0B6-4744-A556-E5667DF823B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {82D6EBAF-B0B6-4744-A556-E5667DF823B7}.Release|Any CPU.Build.0 = Release|Any CPU + {82D6EBAF-B0B6-4744-A556-E5667DF823B7}.Release|x64.ActiveCfg = Release|Any CPU + {82D6EBAF-B0B6-4744-A556-E5667DF823B7}.Release|x64.Build.0 = Release|Any CPU + {82D6EBAF-B0B6-4744-A556-E5667DF823B7}.Release|x86.ActiveCfg = Release|Any CPU + {82D6EBAF-B0B6-4744-A556-E5667DF823B7}.Release|x86.Build.0 = Release|Any CPU + {410366DF-4121-44DE-923D-218ACBA2F8CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {410366DF-4121-44DE-923D-218ACBA2F8CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {410366DF-4121-44DE-923D-218ACBA2F8CC}.Debug|x64.ActiveCfg = Debug|Any CPU + {410366DF-4121-44DE-923D-218ACBA2F8CC}.Debug|x64.Build.0 = Debug|Any CPU + {410366DF-4121-44DE-923D-218ACBA2F8CC}.Debug|x86.ActiveCfg = Debug|Any CPU + {410366DF-4121-44DE-923D-218ACBA2F8CC}.Debug|x86.Build.0 = Debug|Any CPU + {410366DF-4121-44DE-923D-218ACBA2F8CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {410366DF-4121-44DE-923D-218ACBA2F8CC}.Release|Any CPU.Build.0 = Release|Any CPU + {410366DF-4121-44DE-923D-218ACBA2F8CC}.Release|x64.ActiveCfg = Release|Any CPU + {410366DF-4121-44DE-923D-218ACBA2F8CC}.Release|x64.Build.0 = Release|Any CPU + {410366DF-4121-44DE-923D-218ACBA2F8CC}.Release|x86.ActiveCfg = Release|Any CPU + {410366DF-4121-44DE-923D-218ACBA2F8CC}.Release|x86.Build.0 = Release|Any CPU + {A8E066A4-00E7-4209-BB1E-0BD9A2FBAC8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8E066A4-00E7-4209-BB1E-0BD9A2FBAC8E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8E066A4-00E7-4209-BB1E-0BD9A2FBAC8E}.Debug|x64.ActiveCfg = Debug|Any CPU + {A8E066A4-00E7-4209-BB1E-0BD9A2FBAC8E}.Debug|x64.Build.0 = Debug|Any CPU + {A8E066A4-00E7-4209-BB1E-0BD9A2FBAC8E}.Debug|x86.ActiveCfg = Debug|Any CPU + {A8E066A4-00E7-4209-BB1E-0BD9A2FBAC8E}.Debug|x86.Build.0 = Debug|Any CPU + {A8E066A4-00E7-4209-BB1E-0BD9A2FBAC8E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8E066A4-00E7-4209-BB1E-0BD9A2FBAC8E}.Release|Any CPU.Build.0 = Release|Any CPU + {A8E066A4-00E7-4209-BB1E-0BD9A2FBAC8E}.Release|x64.ActiveCfg = Release|Any CPU + {A8E066A4-00E7-4209-BB1E-0BD9A2FBAC8E}.Release|x64.Build.0 = Release|Any CPU + {A8E066A4-00E7-4209-BB1E-0BD9A2FBAC8E}.Release|x86.ActiveCfg = Release|Any CPU + {A8E066A4-00E7-4209-BB1E-0BD9A2FBAC8E}.Release|x86.Build.0 = Release|Any CPU + {656422FD-47DA-4F06-AB76-57FF1F77B040}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {656422FD-47DA-4F06-AB76-57FF1F77B040}.Debug|Any CPU.Build.0 = Debug|Any CPU + {656422FD-47DA-4F06-AB76-57FF1F77B040}.Debug|x64.ActiveCfg = Debug|Any CPU + {656422FD-47DA-4F06-AB76-57FF1F77B040}.Debug|x64.Build.0 = Debug|Any CPU + {656422FD-47DA-4F06-AB76-57FF1F77B040}.Debug|x86.ActiveCfg = Debug|Any CPU + {656422FD-47DA-4F06-AB76-57FF1F77B040}.Debug|x86.Build.0 = Debug|Any CPU + {656422FD-47DA-4F06-AB76-57FF1F77B040}.Release|Any CPU.ActiveCfg = Release|Any CPU + {656422FD-47DA-4F06-AB76-57FF1F77B040}.Release|Any CPU.Build.0 = Release|Any CPU + {656422FD-47DA-4F06-AB76-57FF1F77B040}.Release|x64.ActiveCfg = Release|Any CPU + {656422FD-47DA-4F06-AB76-57FF1F77B040}.Release|x64.Build.0 = Release|Any CPU + {656422FD-47DA-4F06-AB76-57FF1F77B040}.Release|x86.ActiveCfg = Release|Any CPU + {656422FD-47DA-4F06-AB76-57FF1F77B040}.Release|x86.Build.0 = Release|Any CPU + {502B8111-F428-4584-AE44-D1AD3B4F16B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {502B8111-F428-4584-AE44-D1AD3B4F16B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {502B8111-F428-4584-AE44-D1AD3B4F16B5}.Debug|x64.ActiveCfg = Debug|Any CPU + {502B8111-F428-4584-AE44-D1AD3B4F16B5}.Debug|x64.Build.0 = Debug|Any CPU + {502B8111-F428-4584-AE44-D1AD3B4F16B5}.Debug|x86.ActiveCfg = Debug|Any CPU + {502B8111-F428-4584-AE44-D1AD3B4F16B5}.Debug|x86.Build.0 = Debug|Any CPU + {502B8111-F428-4584-AE44-D1AD3B4F16B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {502B8111-F428-4584-AE44-D1AD3B4F16B5}.Release|Any CPU.Build.0 = Release|Any CPU + {502B8111-F428-4584-AE44-D1AD3B4F16B5}.Release|x64.ActiveCfg = Release|Any CPU + {502B8111-F428-4584-AE44-D1AD3B4F16B5}.Release|x64.Build.0 = Release|Any CPU + {502B8111-F428-4584-AE44-D1AD3B4F16B5}.Release|x86.ActiveCfg = Release|Any CPU + {502B8111-F428-4584-AE44-D1AD3B4F16B5}.Release|x86.Build.0 = Release|Any CPU + {174CF08D-E13D-4904-90C0-CC740C9DE086}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {174CF08D-E13D-4904-90C0-CC740C9DE086}.Debug|Any CPU.Build.0 = Debug|Any CPU + {174CF08D-E13D-4904-90C0-CC740C9DE086}.Debug|x64.ActiveCfg = Debug|Any CPU + {174CF08D-E13D-4904-90C0-CC740C9DE086}.Debug|x64.Build.0 = Debug|Any CPU + {174CF08D-E13D-4904-90C0-CC740C9DE086}.Debug|x86.ActiveCfg = Debug|Any CPU + {174CF08D-E13D-4904-90C0-CC740C9DE086}.Debug|x86.Build.0 = Debug|Any CPU + {174CF08D-E13D-4904-90C0-CC740C9DE086}.Release|Any CPU.ActiveCfg = Release|Any CPU + {174CF08D-E13D-4904-90C0-CC740C9DE086}.Release|Any CPU.Build.0 = Release|Any CPU + {174CF08D-E13D-4904-90C0-CC740C9DE086}.Release|x64.ActiveCfg = Release|Any CPU + {174CF08D-E13D-4904-90C0-CC740C9DE086}.Release|x64.Build.0 = Release|Any CPU + {174CF08D-E13D-4904-90C0-CC740C9DE086}.Release|x86.ActiveCfg = Release|Any CPU + {174CF08D-E13D-4904-90C0-CC740C9DE086}.Release|x86.Build.0 = Release|Any CPU + {853E3B54-DB8A-4A82-8565-E6103130DA3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {853E3B54-DB8A-4A82-8565-E6103130DA3C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {853E3B54-DB8A-4A82-8565-E6103130DA3C}.Debug|x64.ActiveCfg = Debug|Any CPU + {853E3B54-DB8A-4A82-8565-E6103130DA3C}.Debug|x64.Build.0 = Debug|Any CPU + {853E3B54-DB8A-4A82-8565-E6103130DA3C}.Debug|x86.ActiveCfg = Debug|Any CPU + {853E3B54-DB8A-4A82-8565-E6103130DA3C}.Debug|x86.Build.0 = Debug|Any CPU + {853E3B54-DB8A-4A82-8565-E6103130DA3C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {853E3B54-DB8A-4A82-8565-E6103130DA3C}.Release|Any CPU.Build.0 = Release|Any CPU + {853E3B54-DB8A-4A82-8565-E6103130DA3C}.Release|x64.ActiveCfg = Release|Any CPU + {853E3B54-DB8A-4A82-8565-E6103130DA3C}.Release|x64.Build.0 = Release|Any CPU + {853E3B54-DB8A-4A82-8565-E6103130DA3C}.Release|x86.ActiveCfg = Release|Any CPU + {853E3B54-DB8A-4A82-8565-E6103130DA3C}.Release|x86.Build.0 = Release|Any CPU + {B8DC7FD1-782F-4246-B096-836D82B29A03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B8DC7FD1-782F-4246-B096-836D82B29A03}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B8DC7FD1-782F-4246-B096-836D82B29A03}.Debug|x64.ActiveCfg = Debug|Any CPU + {B8DC7FD1-782F-4246-B096-836D82B29A03}.Debug|x64.Build.0 = Debug|Any CPU + {B8DC7FD1-782F-4246-B096-836D82B29A03}.Debug|x86.ActiveCfg = Debug|Any CPU + {B8DC7FD1-782F-4246-B096-836D82B29A03}.Debug|x86.Build.0 = Debug|Any CPU + {B8DC7FD1-782F-4246-B096-836D82B29A03}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B8DC7FD1-782F-4246-B096-836D82B29A03}.Release|Any CPU.Build.0 = Release|Any CPU + {B8DC7FD1-782F-4246-B096-836D82B29A03}.Release|x64.ActiveCfg = Release|Any CPU + {B8DC7FD1-782F-4246-B096-836D82B29A03}.Release|x64.Build.0 = Release|Any CPU + {B8DC7FD1-782F-4246-B096-836D82B29A03}.Release|x86.ActiveCfg = Release|Any CPU + {B8DC7FD1-782F-4246-B096-836D82B29A03}.Release|x86.Build.0 = Release|Any CPU + {8AE239F7-A0B1-459A-AF6F-FB1501544C2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8AE239F7-A0B1-459A-AF6F-FB1501544C2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8AE239F7-A0B1-459A-AF6F-FB1501544C2B}.Debug|x64.ActiveCfg = Debug|Any CPU + {8AE239F7-A0B1-459A-AF6F-FB1501544C2B}.Debug|x64.Build.0 = Debug|Any CPU + {8AE239F7-A0B1-459A-AF6F-FB1501544C2B}.Debug|x86.ActiveCfg = Debug|Any CPU + {8AE239F7-A0B1-459A-AF6F-FB1501544C2B}.Debug|x86.Build.0 = Debug|Any CPU + {8AE239F7-A0B1-459A-AF6F-FB1501544C2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8AE239F7-A0B1-459A-AF6F-FB1501544C2B}.Release|Any CPU.Build.0 = Release|Any CPU + {8AE239F7-A0B1-459A-AF6F-FB1501544C2B}.Release|x64.ActiveCfg = Release|Any CPU + {8AE239F7-A0B1-459A-AF6F-FB1501544C2B}.Release|x64.Build.0 = Release|Any CPU + {8AE239F7-A0B1-459A-AF6F-FB1501544C2B}.Release|x86.ActiveCfg = Release|Any CPU + {8AE239F7-A0B1-459A-AF6F-FB1501544C2B}.Release|x86.Build.0 = Release|Any CPU + {FB7DB850-11DB-4F7B-9003-13C145442C73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB7DB850-11DB-4F7B-9003-13C145442C73}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB7DB850-11DB-4F7B-9003-13C145442C73}.Debug|x64.ActiveCfg = Debug|Any CPU + {FB7DB850-11DB-4F7B-9003-13C145442C73}.Debug|x64.Build.0 = Debug|Any CPU + {FB7DB850-11DB-4F7B-9003-13C145442C73}.Debug|x86.ActiveCfg = Debug|Any CPU + {FB7DB850-11DB-4F7B-9003-13C145442C73}.Debug|x86.Build.0 = Debug|Any CPU + {FB7DB850-11DB-4F7B-9003-13C145442C73}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB7DB850-11DB-4F7B-9003-13C145442C73}.Release|Any CPU.Build.0 = Release|Any CPU + {FB7DB850-11DB-4F7B-9003-13C145442C73}.Release|x64.ActiveCfg = Release|Any CPU + {FB7DB850-11DB-4F7B-9003-13C145442C73}.Release|x64.Build.0 = Release|Any CPU + {FB7DB850-11DB-4F7B-9003-13C145442C73}.Release|x86.ActiveCfg = Release|Any CPU + {FB7DB850-11DB-4F7B-9003-13C145442C73}.Release|x86.Build.0 = Release|Any CPU + {23BEB12C-DE15-4B88-96F6-5DBDE2E9BA62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {23BEB12C-DE15-4B88-96F6-5DBDE2E9BA62}.Debug|Any CPU.Build.0 = Debug|Any CPU + {23BEB12C-DE15-4B88-96F6-5DBDE2E9BA62}.Debug|x64.ActiveCfg = Debug|Any CPU + {23BEB12C-DE15-4B88-96F6-5DBDE2E9BA62}.Debug|x64.Build.0 = Debug|Any CPU + {23BEB12C-DE15-4B88-96F6-5DBDE2E9BA62}.Debug|x86.ActiveCfg = Debug|Any CPU + {23BEB12C-DE15-4B88-96F6-5DBDE2E9BA62}.Debug|x86.Build.0 = Debug|Any CPU + {23BEB12C-DE15-4B88-96F6-5DBDE2E9BA62}.Release|Any CPU.ActiveCfg = Release|Any CPU + {23BEB12C-DE15-4B88-96F6-5DBDE2E9BA62}.Release|Any CPU.Build.0 = Release|Any CPU + {23BEB12C-DE15-4B88-96F6-5DBDE2E9BA62}.Release|x64.ActiveCfg = Release|Any CPU + {23BEB12C-DE15-4B88-96F6-5DBDE2E9BA62}.Release|x64.Build.0 = Release|Any CPU + {23BEB12C-DE15-4B88-96F6-5DBDE2E9BA62}.Release|x86.ActiveCfg = Release|Any CPU + {23BEB12C-DE15-4B88-96F6-5DBDE2E9BA62}.Release|x86.Build.0 = Release|Any CPU + {DF2456D1-0CFF-4213-9E8F-D666DAEA2E54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF2456D1-0CFF-4213-9E8F-D666DAEA2E54}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF2456D1-0CFF-4213-9E8F-D666DAEA2E54}.Debug|x64.ActiveCfg = Debug|Any CPU + {DF2456D1-0CFF-4213-9E8F-D666DAEA2E54}.Debug|x64.Build.0 = Debug|Any CPU + {DF2456D1-0CFF-4213-9E8F-D666DAEA2E54}.Debug|x86.ActiveCfg = Debug|Any CPU + {DF2456D1-0CFF-4213-9E8F-D666DAEA2E54}.Debug|x86.Build.0 = Debug|Any CPU + {DF2456D1-0CFF-4213-9E8F-D666DAEA2E54}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DF2456D1-0CFF-4213-9E8F-D666DAEA2E54}.Release|Any CPU.Build.0 = Release|Any CPU + {DF2456D1-0CFF-4213-9E8F-D666DAEA2E54}.Release|x64.ActiveCfg = Release|Any CPU + {DF2456D1-0CFF-4213-9E8F-D666DAEA2E54}.Release|x64.Build.0 = Release|Any CPU + {DF2456D1-0CFF-4213-9E8F-D666DAEA2E54}.Release|x86.ActiveCfg = Release|Any CPU + {DF2456D1-0CFF-4213-9E8F-D666DAEA2E54}.Release|x86.Build.0 = Release|Any CPU + {1980F468-08B9-4159-B63D-4197EE6FC83B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1980F468-08B9-4159-B63D-4197EE6FC83B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1980F468-08B9-4159-B63D-4197EE6FC83B}.Debug|x64.ActiveCfg = Debug|Any CPU + {1980F468-08B9-4159-B63D-4197EE6FC83B}.Debug|x64.Build.0 = Debug|Any CPU + {1980F468-08B9-4159-B63D-4197EE6FC83B}.Debug|x86.ActiveCfg = Debug|Any CPU + {1980F468-08B9-4159-B63D-4197EE6FC83B}.Debug|x86.Build.0 = Debug|Any CPU + {1980F468-08B9-4159-B63D-4197EE6FC83B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1980F468-08B9-4159-B63D-4197EE6FC83B}.Release|Any CPU.Build.0 = Release|Any CPU + {1980F468-08B9-4159-B63D-4197EE6FC83B}.Release|x64.ActiveCfg = Release|Any CPU + {1980F468-08B9-4159-B63D-4197EE6FC83B}.Release|x64.Build.0 = Release|Any CPU + {1980F468-08B9-4159-B63D-4197EE6FC83B}.Release|x86.ActiveCfg = Release|Any CPU + {1980F468-08B9-4159-B63D-4197EE6FC83B}.Release|x86.Build.0 = Release|Any CPU + {957FCED2-6B1E-490E-97B4-2B123E565785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {957FCED2-6B1E-490E-97B4-2B123E565785}.Debug|Any CPU.Build.0 = Debug|Any CPU + {957FCED2-6B1E-490E-97B4-2B123E565785}.Debug|x64.ActiveCfg = Debug|Any CPU + {957FCED2-6B1E-490E-97B4-2B123E565785}.Debug|x64.Build.0 = Debug|Any CPU + {957FCED2-6B1E-490E-97B4-2B123E565785}.Debug|x86.ActiveCfg = Debug|Any CPU + {957FCED2-6B1E-490E-97B4-2B123E565785}.Debug|x86.Build.0 = Debug|Any CPU + {957FCED2-6B1E-490E-97B4-2B123E565785}.Release|Any CPU.ActiveCfg = Release|Any CPU + {957FCED2-6B1E-490E-97B4-2B123E565785}.Release|Any CPU.Build.0 = Release|Any CPU + {957FCED2-6B1E-490E-97B4-2B123E565785}.Release|x64.ActiveCfg = Release|Any CPU + {957FCED2-6B1E-490E-97B4-2B123E565785}.Release|x64.Build.0 = Release|Any CPU + {957FCED2-6B1E-490E-97B4-2B123E565785}.Release|x86.ActiveCfg = Release|Any CPU + {957FCED2-6B1E-490E-97B4-2B123E565785}.Release|x86.Build.0 = Release|Any CPU + {C67F2919-4530-4EAF-B1A1-CCED621BCCFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C67F2919-4530-4EAF-B1A1-CCED621BCCFE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C67F2919-4530-4EAF-B1A1-CCED621BCCFE}.Debug|x64.ActiveCfg = Debug|Any CPU + {C67F2919-4530-4EAF-B1A1-CCED621BCCFE}.Debug|x64.Build.0 = Debug|Any CPU + {C67F2919-4530-4EAF-B1A1-CCED621BCCFE}.Debug|x86.ActiveCfg = Debug|Any CPU + {C67F2919-4530-4EAF-B1A1-CCED621BCCFE}.Debug|x86.Build.0 = Debug|Any CPU + {C67F2919-4530-4EAF-B1A1-CCED621BCCFE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C67F2919-4530-4EAF-B1A1-CCED621BCCFE}.Release|Any CPU.Build.0 = Release|Any CPU + {C67F2919-4530-4EAF-B1A1-CCED621BCCFE}.Release|x64.ActiveCfg = Release|Any CPU + {C67F2919-4530-4EAF-B1A1-CCED621BCCFE}.Release|x64.Build.0 = Release|Any CPU + {C67F2919-4530-4EAF-B1A1-CCED621BCCFE}.Release|x86.ActiveCfg = Release|Any CPU + {C67F2919-4530-4EAF-B1A1-CCED621BCCFE}.Release|x86.Build.0 = Release|Any CPU + {8CE6E5B6-9BE6-4E8E-B17A-A9CA0148842E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8CE6E5B6-9BE6-4E8E-B17A-A9CA0148842E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8CE6E5B6-9BE6-4E8E-B17A-A9CA0148842E}.Debug|x64.ActiveCfg = Debug|Any CPU + {8CE6E5B6-9BE6-4E8E-B17A-A9CA0148842E}.Debug|x64.Build.0 = Debug|Any CPU + {8CE6E5B6-9BE6-4E8E-B17A-A9CA0148842E}.Debug|x86.ActiveCfg = Debug|Any CPU + {8CE6E5B6-9BE6-4E8E-B17A-A9CA0148842E}.Debug|x86.Build.0 = Debug|Any CPU + {8CE6E5B6-9BE6-4E8E-B17A-A9CA0148842E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8CE6E5B6-9BE6-4E8E-B17A-A9CA0148842E}.Release|Any CPU.Build.0 = Release|Any CPU + {8CE6E5B6-9BE6-4E8E-B17A-A9CA0148842E}.Release|x64.ActiveCfg = Release|Any CPU + {8CE6E5B6-9BE6-4E8E-B17A-A9CA0148842E}.Release|x64.Build.0 = Release|Any CPU + {8CE6E5B6-9BE6-4E8E-B17A-A9CA0148842E}.Release|x86.ActiveCfg = Release|Any CPU + {8CE6E5B6-9BE6-4E8E-B17A-A9CA0148842E}.Release|x86.Build.0 = Release|Any CPU + {035C2D5E-18DD-4CFC-98E2-0A8BF69A1511}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {035C2D5E-18DD-4CFC-98E2-0A8BF69A1511}.Debug|Any CPU.Build.0 = Debug|Any CPU + {035C2D5E-18DD-4CFC-98E2-0A8BF69A1511}.Debug|x64.ActiveCfg = Debug|Any CPU + {035C2D5E-18DD-4CFC-98E2-0A8BF69A1511}.Debug|x64.Build.0 = Debug|Any CPU + {035C2D5E-18DD-4CFC-98E2-0A8BF69A1511}.Debug|x86.ActiveCfg = Debug|Any CPU + {035C2D5E-18DD-4CFC-98E2-0A8BF69A1511}.Debug|x86.Build.0 = Debug|Any CPU + {035C2D5E-18DD-4CFC-98E2-0A8BF69A1511}.Release|Any CPU.ActiveCfg = Release|Any CPU + {035C2D5E-18DD-4CFC-98E2-0A8BF69A1511}.Release|Any CPU.Build.0 = Release|Any CPU + {035C2D5E-18DD-4CFC-98E2-0A8BF69A1511}.Release|x64.ActiveCfg = Release|Any CPU + {035C2D5E-18DD-4CFC-98E2-0A8BF69A1511}.Release|x64.Build.0 = Release|Any CPU + {035C2D5E-18DD-4CFC-98E2-0A8BF69A1511}.Release|x86.ActiveCfg = Release|Any CPU + {035C2D5E-18DD-4CFC-98E2-0A8BF69A1511}.Release|x86.Build.0 = Release|Any CPU + {2FAEDBBC-6F75-46AC-A281-F507412EDF6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2FAEDBBC-6F75-46AC-A281-F507412EDF6A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2FAEDBBC-6F75-46AC-A281-F507412EDF6A}.Debug|x64.ActiveCfg = Debug|Any CPU + {2FAEDBBC-6F75-46AC-A281-F507412EDF6A}.Debug|x64.Build.0 = Debug|Any CPU + {2FAEDBBC-6F75-46AC-A281-F507412EDF6A}.Debug|x86.ActiveCfg = Debug|Any CPU + {2FAEDBBC-6F75-46AC-A281-F507412EDF6A}.Debug|x86.Build.0 = Debug|Any CPU + {2FAEDBBC-6F75-46AC-A281-F507412EDF6A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2FAEDBBC-6F75-46AC-A281-F507412EDF6A}.Release|Any CPU.Build.0 = Release|Any CPU + {2FAEDBBC-6F75-46AC-A281-F507412EDF6A}.Release|x64.ActiveCfg = Release|Any CPU + {2FAEDBBC-6F75-46AC-A281-F507412EDF6A}.Release|x64.Build.0 = Release|Any CPU + {2FAEDBBC-6F75-46AC-A281-F507412EDF6A}.Release|x86.ActiveCfg = Release|Any CPU + {2FAEDBBC-6F75-46AC-A281-F507412EDF6A}.Release|x86.Build.0 = Release|Any CPU + {F00538BE-2AF8-4388-BF7C-59934838B7BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F00538BE-2AF8-4388-BF7C-59934838B7BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F00538BE-2AF8-4388-BF7C-59934838B7BD}.Debug|x64.ActiveCfg = Debug|Any CPU + {F00538BE-2AF8-4388-BF7C-59934838B7BD}.Debug|x64.Build.0 = Debug|Any CPU + {F00538BE-2AF8-4388-BF7C-59934838B7BD}.Debug|x86.ActiveCfg = Debug|Any CPU + {F00538BE-2AF8-4388-BF7C-59934838B7BD}.Debug|x86.Build.0 = Debug|Any CPU + {F00538BE-2AF8-4388-BF7C-59934838B7BD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F00538BE-2AF8-4388-BF7C-59934838B7BD}.Release|Any CPU.Build.0 = Release|Any CPU + {F00538BE-2AF8-4388-BF7C-59934838B7BD}.Release|x64.ActiveCfg = Release|Any CPU + {F00538BE-2AF8-4388-BF7C-59934838B7BD}.Release|x64.Build.0 = Release|Any CPU + {F00538BE-2AF8-4388-BF7C-59934838B7BD}.Release|x86.ActiveCfg = Release|Any CPU + {F00538BE-2AF8-4388-BF7C-59934838B7BD}.Release|x86.Build.0 = Release|Any CPU + {06A50D0C-EAF4-4599-83A9-B23C0756ADF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {06A50D0C-EAF4-4599-83A9-B23C0756ADF5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {06A50D0C-EAF4-4599-83A9-B23C0756ADF5}.Debug|x64.ActiveCfg = Debug|Any CPU + {06A50D0C-EAF4-4599-83A9-B23C0756ADF5}.Debug|x64.Build.0 = Debug|Any CPU + {06A50D0C-EAF4-4599-83A9-B23C0756ADF5}.Debug|x86.ActiveCfg = Debug|Any CPU + {06A50D0C-EAF4-4599-83A9-B23C0756ADF5}.Debug|x86.Build.0 = Debug|Any CPU + {06A50D0C-EAF4-4599-83A9-B23C0756ADF5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {06A50D0C-EAF4-4599-83A9-B23C0756ADF5}.Release|Any CPU.Build.0 = Release|Any CPU + {06A50D0C-EAF4-4599-83A9-B23C0756ADF5}.Release|x64.ActiveCfg = Release|Any CPU + {06A50D0C-EAF4-4599-83A9-B23C0756ADF5}.Release|x64.Build.0 = Release|Any CPU + {06A50D0C-EAF4-4599-83A9-B23C0756ADF5}.Release|x86.ActiveCfg = Release|Any CPU + {06A50D0C-EAF4-4599-83A9-B23C0756ADF5}.Release|x86.Build.0 = Release|Any CPU + {86DEDD76-E85D-44C9-BDF6-7D3299F0AED7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86DEDD76-E85D-44C9-BDF6-7D3299F0AED7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86DEDD76-E85D-44C9-BDF6-7D3299F0AED7}.Debug|x64.ActiveCfg = Debug|Any CPU + {86DEDD76-E85D-44C9-BDF6-7D3299F0AED7}.Debug|x64.Build.0 = Debug|Any CPU + {86DEDD76-E85D-44C9-BDF6-7D3299F0AED7}.Debug|x86.ActiveCfg = Debug|Any CPU + {86DEDD76-E85D-44C9-BDF6-7D3299F0AED7}.Debug|x86.Build.0 = Debug|Any CPU + {86DEDD76-E85D-44C9-BDF6-7D3299F0AED7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86DEDD76-E85D-44C9-BDF6-7D3299F0AED7}.Release|Any CPU.Build.0 = Release|Any CPU + {86DEDD76-E85D-44C9-BDF6-7D3299F0AED7}.Release|x64.ActiveCfg = Release|Any CPU + {86DEDD76-E85D-44C9-BDF6-7D3299F0AED7}.Release|x64.Build.0 = Release|Any CPU + {86DEDD76-E85D-44C9-BDF6-7D3299F0AED7}.Release|x86.ActiveCfg = Release|Any CPU + {86DEDD76-E85D-44C9-BDF6-7D3299F0AED7}.Release|x86.Build.0 = Release|Any CPU + {994DA72D-7712-4AF4-A1FC-6E53CFD9ACD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {994DA72D-7712-4AF4-A1FC-6E53CFD9ACD9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {994DA72D-7712-4AF4-A1FC-6E53CFD9ACD9}.Debug|x64.ActiveCfg = Debug|Any CPU + {994DA72D-7712-4AF4-A1FC-6E53CFD9ACD9}.Debug|x64.Build.0 = Debug|Any CPU + {994DA72D-7712-4AF4-A1FC-6E53CFD9ACD9}.Debug|x86.ActiveCfg = Debug|Any CPU + {994DA72D-7712-4AF4-A1FC-6E53CFD9ACD9}.Debug|x86.Build.0 = Debug|Any CPU + {994DA72D-7712-4AF4-A1FC-6E53CFD9ACD9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {994DA72D-7712-4AF4-A1FC-6E53CFD9ACD9}.Release|Any CPU.Build.0 = Release|Any CPU + {994DA72D-7712-4AF4-A1FC-6E53CFD9ACD9}.Release|x64.ActiveCfg = Release|Any CPU + {994DA72D-7712-4AF4-A1FC-6E53CFD9ACD9}.Release|x64.Build.0 = Release|Any CPU + {994DA72D-7712-4AF4-A1FC-6E53CFD9ACD9}.Release|x86.ActiveCfg = Release|Any CPU + {994DA72D-7712-4AF4-A1FC-6E53CFD9ACD9}.Release|x86.Build.0 = Release|Any CPU + {8F625A63-FFCD-4F8F-8C5C-97252728AA8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F625A63-FFCD-4F8F-8C5C-97252728AA8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F625A63-FFCD-4F8F-8C5C-97252728AA8B}.Debug|x64.ActiveCfg = Debug|Any CPU + {8F625A63-FFCD-4F8F-8C5C-97252728AA8B}.Debug|x64.Build.0 = Debug|Any CPU + {8F625A63-FFCD-4F8F-8C5C-97252728AA8B}.Debug|x86.ActiveCfg = Debug|Any CPU + {8F625A63-FFCD-4F8F-8C5C-97252728AA8B}.Debug|x86.Build.0 = Debug|Any CPU + {8F625A63-FFCD-4F8F-8C5C-97252728AA8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F625A63-FFCD-4F8F-8C5C-97252728AA8B}.Release|Any CPU.Build.0 = Release|Any CPU + {8F625A63-FFCD-4F8F-8C5C-97252728AA8B}.Release|x64.ActiveCfg = Release|Any CPU + {8F625A63-FFCD-4F8F-8C5C-97252728AA8B}.Release|x64.Build.0 = Release|Any CPU + {8F625A63-FFCD-4F8F-8C5C-97252728AA8B}.Release|x86.ActiveCfg = Release|Any CPU + {8F625A63-FFCD-4F8F-8C5C-97252728AA8B}.Release|x86.Build.0 = Release|Any CPU + {BC735304-7981-4854-A9EE-00FD55943BF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BC735304-7981-4854-A9EE-00FD55943BF4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BC735304-7981-4854-A9EE-00FD55943BF4}.Debug|x64.ActiveCfg = Debug|Any CPU + {BC735304-7981-4854-A9EE-00FD55943BF4}.Debug|x64.Build.0 = Debug|Any CPU + {BC735304-7981-4854-A9EE-00FD55943BF4}.Debug|x86.ActiveCfg = Debug|Any CPU + {BC735304-7981-4854-A9EE-00FD55943BF4}.Debug|x86.Build.0 = Debug|Any CPU + {BC735304-7981-4854-A9EE-00FD55943BF4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BC735304-7981-4854-A9EE-00FD55943BF4}.Release|Any CPU.Build.0 = Release|Any CPU + {BC735304-7981-4854-A9EE-00FD55943BF4}.Release|x64.ActiveCfg = Release|Any CPU + {BC735304-7981-4854-A9EE-00FD55943BF4}.Release|x64.Build.0 = Release|Any CPU + {BC735304-7981-4854-A9EE-00FD55943BF4}.Release|x86.ActiveCfg = Release|Any CPU + {BC735304-7981-4854-A9EE-00FD55943BF4}.Release|x86.Build.0 = Release|Any CPU + {EF4C2EAF-8CF8-4E43-BBAA-9A2017C43B80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF4C2EAF-8CF8-4E43-BBAA-9A2017C43B80}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF4C2EAF-8CF8-4E43-BBAA-9A2017C43B80}.Debug|x64.ActiveCfg = Debug|Any CPU + {EF4C2EAF-8CF8-4E43-BBAA-9A2017C43B80}.Debug|x64.Build.0 = Debug|Any CPU + {EF4C2EAF-8CF8-4E43-BBAA-9A2017C43B80}.Debug|x86.ActiveCfg = Debug|Any CPU + {EF4C2EAF-8CF8-4E43-BBAA-9A2017C43B80}.Debug|x86.Build.0 = Debug|Any CPU + {EF4C2EAF-8CF8-4E43-BBAA-9A2017C43B80}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF4C2EAF-8CF8-4E43-BBAA-9A2017C43B80}.Release|Any CPU.Build.0 = Release|Any CPU + {EF4C2EAF-8CF8-4E43-BBAA-9A2017C43B80}.Release|x64.ActiveCfg = Release|Any CPU + {EF4C2EAF-8CF8-4E43-BBAA-9A2017C43B80}.Release|x64.Build.0 = Release|Any CPU + {EF4C2EAF-8CF8-4E43-BBAA-9A2017C43B80}.Release|x86.ActiveCfg = Release|Any CPU + {EF4C2EAF-8CF8-4E43-BBAA-9A2017C43B80}.Release|x86.Build.0 = Release|Any CPU + {E6717F55-79BC-494A-84B1-215491824F20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E6717F55-79BC-494A-84B1-215491824F20}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E6717F55-79BC-494A-84B1-215491824F20}.Debug|x64.ActiveCfg = Debug|Any CPU + {E6717F55-79BC-494A-84B1-215491824F20}.Debug|x64.Build.0 = Debug|Any CPU + {E6717F55-79BC-494A-84B1-215491824F20}.Debug|x86.ActiveCfg = Debug|Any CPU + {E6717F55-79BC-494A-84B1-215491824F20}.Debug|x86.Build.0 = Debug|Any CPU + {E6717F55-79BC-494A-84B1-215491824F20}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E6717F55-79BC-494A-84B1-215491824F20}.Release|Any CPU.Build.0 = Release|Any CPU + {E6717F55-79BC-494A-84B1-215491824F20}.Release|x64.ActiveCfg = Release|Any CPU + {E6717F55-79BC-494A-84B1-215491824F20}.Release|x64.Build.0 = Release|Any CPU + {E6717F55-79BC-494A-84B1-215491824F20}.Release|x86.ActiveCfg = Release|Any CPU + {E6717F55-79BC-494A-84B1-215491824F20}.Release|x86.Build.0 = Release|Any CPU + {7CA07B2A-F612-47F0-8F4F-E7771BF9DED1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7CA07B2A-F612-47F0-8F4F-E7771BF9DED1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7CA07B2A-F612-47F0-8F4F-E7771BF9DED1}.Debug|x64.ActiveCfg = Debug|Any CPU + {7CA07B2A-F612-47F0-8F4F-E7771BF9DED1}.Debug|x64.Build.0 = Debug|Any CPU + {7CA07B2A-F612-47F0-8F4F-E7771BF9DED1}.Debug|x86.ActiveCfg = Debug|Any CPU + {7CA07B2A-F612-47F0-8F4F-E7771BF9DED1}.Debug|x86.Build.0 = Debug|Any CPU + {7CA07B2A-F612-47F0-8F4F-E7771BF9DED1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7CA07B2A-F612-47F0-8F4F-E7771BF9DED1}.Release|Any CPU.Build.0 = Release|Any CPU + {7CA07B2A-F612-47F0-8F4F-E7771BF9DED1}.Release|x64.ActiveCfg = Release|Any CPU + {7CA07B2A-F612-47F0-8F4F-E7771BF9DED1}.Release|x64.Build.0 = Release|Any CPU + {7CA07B2A-F612-47F0-8F4F-E7771BF9DED1}.Release|x86.ActiveCfg = Release|Any CPU + {7CA07B2A-F612-47F0-8F4F-E7771BF9DED1}.Release|x86.Build.0 = Release|Any CPU + {FA121F56-04BB-46A1-B8C1-0C282295584B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FA121F56-04BB-46A1-B8C1-0C282295584B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FA121F56-04BB-46A1-B8C1-0C282295584B}.Debug|x64.ActiveCfg = Debug|Any CPU + {FA121F56-04BB-46A1-B8C1-0C282295584B}.Debug|x64.Build.0 = Debug|Any CPU + {FA121F56-04BB-46A1-B8C1-0C282295584B}.Debug|x86.ActiveCfg = Debug|Any CPU + {FA121F56-04BB-46A1-B8C1-0C282295584B}.Debug|x86.Build.0 = Debug|Any CPU + {FA121F56-04BB-46A1-B8C1-0C282295584B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FA121F56-04BB-46A1-B8C1-0C282295584B}.Release|Any CPU.Build.0 = Release|Any CPU + {FA121F56-04BB-46A1-B8C1-0C282295584B}.Release|x64.ActiveCfg = Release|Any CPU + {FA121F56-04BB-46A1-B8C1-0C282295584B}.Release|x64.Build.0 = Release|Any CPU + {FA121F56-04BB-46A1-B8C1-0C282295584B}.Release|x86.ActiveCfg = Release|Any CPU + {FA121F56-04BB-46A1-B8C1-0C282295584B}.Release|x86.Build.0 = Release|Any CPU + {3C0A98D5-DAFF-4FF1-9A59-F7409CB66754}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3C0A98D5-DAFF-4FF1-9A59-F7409CB66754}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C0A98D5-DAFF-4FF1-9A59-F7409CB66754}.Debug|x64.ActiveCfg = Debug|Any CPU + {3C0A98D5-DAFF-4FF1-9A59-F7409CB66754}.Debug|x64.Build.0 = Debug|Any CPU + {3C0A98D5-DAFF-4FF1-9A59-F7409CB66754}.Debug|x86.ActiveCfg = Debug|Any CPU + {3C0A98D5-DAFF-4FF1-9A59-F7409CB66754}.Debug|x86.Build.0 = Debug|Any CPU + {3C0A98D5-DAFF-4FF1-9A59-F7409CB66754}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3C0A98D5-DAFF-4FF1-9A59-F7409CB66754}.Release|Any CPU.Build.0 = Release|Any CPU + {3C0A98D5-DAFF-4FF1-9A59-F7409CB66754}.Release|x64.ActiveCfg = Release|Any CPU + {3C0A98D5-DAFF-4FF1-9A59-F7409CB66754}.Release|x64.Build.0 = Release|Any CPU + {3C0A98D5-DAFF-4FF1-9A59-F7409CB66754}.Release|x86.ActiveCfg = Release|Any CPU + {3C0A98D5-DAFF-4FF1-9A59-F7409CB66754}.Release|x86.Build.0 = Release|Any CPU + {46B6E125-FF47-4453-9172-5DF695687FDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {46B6E125-FF47-4453-9172-5DF695687FDF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {46B6E125-FF47-4453-9172-5DF695687FDF}.Debug|x64.ActiveCfg = Debug|Any CPU + {46B6E125-FF47-4453-9172-5DF695687FDF}.Debug|x64.Build.0 = Debug|Any CPU + {46B6E125-FF47-4453-9172-5DF695687FDF}.Debug|x86.ActiveCfg = Debug|Any CPU + {46B6E125-FF47-4453-9172-5DF695687FDF}.Debug|x86.Build.0 = Debug|Any CPU + {46B6E125-FF47-4453-9172-5DF695687FDF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {46B6E125-FF47-4453-9172-5DF695687FDF}.Release|Any CPU.Build.0 = Release|Any CPU + {46B6E125-FF47-4453-9172-5DF695687FDF}.Release|x64.ActiveCfg = Release|Any CPU + {46B6E125-FF47-4453-9172-5DF695687FDF}.Release|x64.Build.0 = Release|Any CPU + {46B6E125-FF47-4453-9172-5DF695687FDF}.Release|x86.ActiveCfg = Release|Any CPU + {46B6E125-FF47-4453-9172-5DF695687FDF}.Release|x86.Build.0 = Release|Any CPU + {07A39741-8656-488A-964C-D8387A1925DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {07A39741-8656-488A-964C-D8387A1925DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {07A39741-8656-488A-964C-D8387A1925DF}.Debug|x64.ActiveCfg = Debug|Any CPU + {07A39741-8656-488A-964C-D8387A1925DF}.Debug|x64.Build.0 = Debug|Any CPU + {07A39741-8656-488A-964C-D8387A1925DF}.Debug|x86.ActiveCfg = Debug|Any CPU + {07A39741-8656-488A-964C-D8387A1925DF}.Debug|x86.Build.0 = Debug|Any CPU + {07A39741-8656-488A-964C-D8387A1925DF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {07A39741-8656-488A-964C-D8387A1925DF}.Release|Any CPU.Build.0 = Release|Any CPU + {07A39741-8656-488A-964C-D8387A1925DF}.Release|x64.ActiveCfg = Release|Any CPU + {07A39741-8656-488A-964C-D8387A1925DF}.Release|x64.Build.0 = Release|Any CPU + {07A39741-8656-488A-964C-D8387A1925DF}.Release|x86.ActiveCfg = Release|Any CPU + {07A39741-8656-488A-964C-D8387A1925DF}.Release|x86.Build.0 = Release|Any CPU + {76924193-4339-4BC8-8C0B-CA5F0A5047FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {76924193-4339-4BC8-8C0B-CA5F0A5047FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76924193-4339-4BC8-8C0B-CA5F0A5047FB}.Debug|x64.ActiveCfg = Debug|Any CPU + {76924193-4339-4BC8-8C0B-CA5F0A5047FB}.Debug|x64.Build.0 = Debug|Any CPU + {76924193-4339-4BC8-8C0B-CA5F0A5047FB}.Debug|x86.ActiveCfg = Debug|Any CPU + {76924193-4339-4BC8-8C0B-CA5F0A5047FB}.Debug|x86.Build.0 = Debug|Any CPU + {76924193-4339-4BC8-8C0B-CA5F0A5047FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {76924193-4339-4BC8-8C0B-CA5F0A5047FB}.Release|Any CPU.Build.0 = Release|Any CPU + {76924193-4339-4BC8-8C0B-CA5F0A5047FB}.Release|x64.ActiveCfg = Release|Any CPU + {76924193-4339-4BC8-8C0B-CA5F0A5047FB}.Release|x64.Build.0 = Release|Any CPU + {76924193-4339-4BC8-8C0B-CA5F0A5047FB}.Release|x86.ActiveCfg = Release|Any CPU + {76924193-4339-4BC8-8C0B-CA5F0A5047FB}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {3576A033-7429-4DE3-BBE3-CD703A4564ED} = {A1B2C3D4-E5F6-7890-ABCD-EF1234567890} {C3CCD71A-37D0-4665-A628-AC6185313A4C} = {A1B2C3D4-E5F6-7890-ABCD-EF1234567890} {04A5E9A6-9EA9-4771-9766-1241F8341A9B} = {A1B2C3D4-E5F6-7890-ABCD-EF1234567890} {97B6CA29-9704-4A44-8879-E7015403E743} = {0C88DD14-F956-CE84-757C-A364CCF449FC} + {EC9257F5-F59F-4F7C-B316-A153DCB9501C} = {A1B2C3D4-E5F6-7890-ABCD-EF1234567890} + {4FBF6A86-DE1B-45E3-A1CE-B1271E927DF8} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {898F86C5-1886-41BE-97DD-46A182261678} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {88836B48-12C3-4CC7-A73E-0245970589CB} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {297D901A-F3D0-4653-B4EB-CBD3D4402612} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {82456D25-7753-4D1C-9ADC-450948B7FAC0} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {E5408C31-60C9-4A60-89D9-840C8C9E5E2F} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {79094B35-C3C6-4332-8A79-6E3D32582059} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {7311835B-6CE8-470E-AFB4-B76424A86096} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {04F673E8-6A3D-4A60-A829-1B851FDB5A4C} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {1826ECD1-D177-4AEF-B821-8B65535CAA46} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {C6D9607E-AB00-4E29-A8A9-8F378B2D755B} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {19462AF9-35D7-43C8-AFA1-39E2FD1C7EBD} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {745684DE-BABB-4BAA-BC36-DA3029E8C99F} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {F58C0BE6-D91A-42F7-A221-4A882911E61C} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {5F1D96A7-8252-4D60-AE6E-A66C045A8A92} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {AEDD4AE8-A831-46EE-AF49-EAC3183D5971} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {F2387E3F-8B78-4FAC-BDAA-A3E2EBC4D889} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {D509243A-E5AA-430D-B47E-8ADC5DA31125} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {7C8AA7A4-E72E-4A21-BC13-D7BA5A2219DC} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {443B5C89-0904-4AE8-B48F-1E92709FBEF3} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {48EA13C9-7FBC-4DCA-85CF-CCB1493A73C8} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {EF1817C6-1FEB-4185-94F3-6BDB9ED1660A} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {1EF56286-3151-4D13-B662-C23CBE7148FE} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {5FF3DBB0-B465-46C5-ADB7-CE8998A6E7BA} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {F743054C-0D7F-4352-9DBD-365C4CFA19C6} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {509EA8AB-5717-4953-87FC-F3EDD041F200} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {A9677606-014A-43A4-83D1-7871D1547862} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {42B441C3-43AD-4D8F-96E0-547D7257FB04} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {DD4CFE16-3F58-4AA0-A995-312AFC55F0B3} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {CC31B699-7B15-4685-8E00-01E2DE6CA88B} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {ED59B9E2-82EC-4984-9A27-E9DFF3B13B60} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {6FC8F397-8F3D-4300-A8B3-6F76B68C959B} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {7B9E64D1-70C9-4462-BE0B-A965A2825919} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {82D6EBAF-B0B6-4744-A556-E5667DF823B7} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {410366DF-4121-44DE-923D-218ACBA2F8CC} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {A8E066A4-00E7-4209-BB1E-0BD9A2FBAC8E} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {656422FD-47DA-4F06-AB76-57FF1F77B040} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {502B8111-F428-4584-AE44-D1AD3B4F16B5} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {174CF08D-E13D-4904-90C0-CC740C9DE086} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {853E3B54-DB8A-4A82-8565-E6103130DA3C} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {B8DC7FD1-782F-4246-B096-836D82B29A03} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {8AE239F7-A0B1-459A-AF6F-FB1501544C2B} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {FB7DB850-11DB-4F7B-9003-13C145442C73} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {23BEB12C-DE15-4B88-96F6-5DBDE2E9BA62} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {DF2456D1-0CFF-4213-9E8F-D666DAEA2E54} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {1980F468-08B9-4159-B63D-4197EE6FC83B} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {957FCED2-6B1E-490E-97B4-2B123E565785} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {C67F2919-4530-4EAF-B1A1-CCED621BCCFE} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {8CE6E5B6-9BE6-4E8E-B17A-A9CA0148842E} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {035C2D5E-18DD-4CFC-98E2-0A8BF69A1511} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {2FAEDBBC-6F75-46AC-A281-F507412EDF6A} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {F00538BE-2AF8-4388-BF7C-59934838B7BD} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {06A50D0C-EAF4-4599-83A9-B23C0756ADF5} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {86DEDD76-E85D-44C9-BDF6-7D3299F0AED7} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {994DA72D-7712-4AF4-A1FC-6E53CFD9ACD9} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {8F625A63-FFCD-4F8F-8C5C-97252728AA8B} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {BC735304-7981-4854-A9EE-00FD55943BF4} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {EF4C2EAF-8CF8-4E43-BBAA-9A2017C43B80} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {E6717F55-79BC-494A-84B1-215491824F20} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {7CA07B2A-F612-47F0-8F4F-E7771BF9DED1} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {FA121F56-04BB-46A1-B8C1-0C282295584B} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {3C0A98D5-DAFF-4FF1-9A59-F7409CB66754} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {46B6E125-FF47-4453-9172-5DF695687FDF} = {EC9257F5-F59F-4F7C-B316-A153DCB9501C} + {07A39741-8656-488A-964C-D8387A1925DF} = {A1B2C3D4-E5F6-7890-ABCD-EF1234567890} + {76924193-4339-4BC8-8C0B-CA5F0A5047FB} = {A1B2C3D4-E5F6-7890-ABCD-EF1234567890} EndGlobalSection EndGlobal diff --git a/README.md b/README.md index 1ae3b1f3..4d4fe976 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ - [Дополнительные файлы](#дополнительные-файлы) - [Временные файлы](#временные-файлы) - [Шаблон имени файла](#шаблон-имени-файла) +- [Архитектура проекта](#архитектура-проекта) - [Полный список параметров](#полный-список-параметров) - [Скриншоты](#скриншоты) - [Лицензия](#лицензия) @@ -82,9 +83,12 @@ https://bookmate.ru/ https://booknet.com/ https://booknet.ua/ https://bookriver.ru/ +https://books.yandex.ru/ https://bookstab.ru/ https://bookstime.ru/ https://bookuruk.com/ +https://boovell.ru/ +https://boosty.to/ https://dark-novels.ru/ https://desu.me/ https://dreame.com/ @@ -94,6 +98,7 @@ https://fanficus.com/ https://fb2.top/ https://ficbook.net/ https://fictionbook.ru/ +https://freedlit.space/ https://hentailib.me/ https://hogwartsnet.ru/ https://hotnovelpub.com/ @@ -104,6 +109,7 @@ https://jaomix.ru/ https://ladylib.top/ https://lanovels.com/ https://libbox.ru/ +https://libking.ru/ https://librebook.me/ https://libst.ru/ https://lightnoveldaily.com/ @@ -116,6 +122,7 @@ https://litres.ru/ https://litsovet.ru/ https://manga.ovh/ https://mangalib.me/ +https://mangalib.org/ https://mangamammy.ru/ https://mir-knig.com/ https://mlate.ru/ @@ -331,12 +338,18 @@ Elib2EbookCli -u https://author.today/work/212721 -f epub --book-name-pattern "{ --- +## Архитектура проекта + +Подробное описание архитектуры проекта, структуры слоёв и используемых паттернов см. в [ARCHITECTURE.md](ARCHITECTURE.md). + +--- + ## Полный список параметров | Параметр | Алиас | Обязательный | По умолчанию | Описание | |-------------------------|:-----:|:------------:|:------------------------------------:|---------------------------------------------------------------| | `--url` | `-u` | ✅ | — | Ссылка на книгу (несколько — через запятую) | -| `--format` | `-f` | ✅ | — | Формат(ы): `epub`, `fb2`, `cbz`, `json`, `txt` | +| `--format` | `-f` | ✅ | — | Формат(ы): `epub`, `fb2`, `cbz`, `json`, `json_lite`, `txt` | | `--login` | `-l` | ❌ | — | Логин для авторизации | | `--password` | `-p` | ❌ | — | Пароль для авторизации | | `--proxy` | — | ❌ | — | Прокси: `(http\|socks4\|socks5)://host:port` | diff --git a/src/Core/Logic/Getters/RanobeNovelsGetter.cs b/src/Core/Logic/Getters/RanobeNovelsGetter.cs deleted file mode 100644 index 16975c18..00000000 --- a/src/Core/Logic/Getters/RanobeNovelsGetter.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System.Net; -using System.Net.Http.Json; -using System.Text.RegularExpressions; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.WuxiaWorld; -using HtmlAgilityPack; -using HtmlAgilityPack.CssSelectors.NetCore; -using Microsoft.Extensions.Logging; - -namespace Core.Logic.Getters; - -public class RanobeNovelsGetter(BookGetterConfig config) : GetterBase(config) -{ - protected override Uri SystemUrl => new("https://ranobe-novels.ru/"); - - public override async Task Get(Uri url) - { - var bookId = GetId(url); - url = SystemUrl.MakeRelativeUri($"/ranobe/{bookId}/"); - var doc = await GetSafety(url); - - var book = new Book(url) - { - Cover = await GetCover(doc, url), Chapters = await FillChapters(doc, url), Title = doc.GetTextBySelector("h1"), Author = new Author("ranobe-novels") - }; - - return book; - } - - private async Task> FillChapters(HtmlDocument doc, Uri url) - { - var result = new List(); - if (Config.Options.NoChapters) - { - return result; - } - - var toc = await GetToc(doc); - if (toc.Count == 0) - { - doc = await Config.Client.GetHtmlDocWithTriesAsync(SystemUrl.MakeRelativeUri($"/{GetId(url)}")); - toc = await GetToc(doc); - } - - foreach (var ranobeChapter in toc) - { - Config.Logger.LogInformation($"Загружаю главу {ranobeChapter.Title.CoverQuotes()}"); - var chapter = new Chapter(); - var chapterDoc = await GetChapter(ranobeChapter.Url); - chapter.Images = await GetImages(chapterDoc, url); - chapter.Content = chapterDoc.DocumentNode.InnerHtml; - chapter.Title = ranobeChapter.Title; - - result.Add(chapter); - } - - return result; - } - - private async Task GetChapter(Uri url) - { - var doc = await GetSafety(url); - return doc.QuerySelector("div.js-full-content").RemoveNodes("> :not(p)").InnerHtml.AsHtmlDoc(); - } - - private async Task> GetToc(HtmlDocument doc) - { - var catId = Regex.Match(doc.ParsedText, "data-cat=\"(?\\d+)\"").Groups["catId"].Value; - var result = new List(); - - foreach (var span in doc.QuerySelectorAll("ul.myUL span.caret[data-id]")) - { - var offset = (int.Parse(span.Attributes["data-id"].Value) - 1) * 100; - var payload = new FormUrlEncodedContent( - new Dictionary - { - ["cat_id"] = catId, ["offset"] = offset.ToString() - }); - - var post = await Config.Client.PostWithTriesAsync(SystemUrl.MakeRelativeUri("/wp-content/themes/ranobe-novels/template-parts/post/menu-query.php"), payload); - var toc = await post.Content.ReadFromJsonAsync(); - result.AddRange(toc.Select((a, i) => new UrlChapter(SystemUrl.MakeRelativeUri(a.PostName), $"Глава {offset + i + 1}"))); - } - - return SliceToc(result, c => c.Title).ToList(); - } - - private Task GetCover(HtmlDocument doc, Uri uri) - { - var imagePath = doc.QuerySelector("meta[property=og:image]")?.Attributes["content"]?.Value; - return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); - } - - private async Task GetSafety(Uri url) - { - var response = await Config.Client.GetWithTriesAsync(url); - await Task.Delay(TimeSpan.FromSeconds(1)); - - while (response == null || response.StatusCode == HttpStatusCode.ServiceUnavailable) - { - Config.Logger.LogInformation("Получен бан от системы. Жду..."); - var errorTimeout = TimeSpan.FromSeconds(30); - response = await Config.Client.GetWithTriesAsync(url, errorTimeout); - await Task.Delay(errorTimeout); - } - - return await response.Content.ReadAsStreamAsync().ContinueWith(t => t.Result.AsHtmlDoc()); - } -} diff --git a/src/Core/Misc/GetterProvider.cs b/src/Core/Misc/GetterProvider.cs deleted file mode 100644 index e3ad1060..00000000 --- a/src/Core/Misc/GetterProvider.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using Core.Configs; -using Core.Logic.Getters; - -namespace Core.Misc; - -public static class GetterProvider -{ - public static GetterBase Get(BookGetterConfig config, Uri url) - { - return Assembly.GetAssembly(typeof(GetterBase))!.GetTypes() - .Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(GetterBase))) - .Select(type => (GetterBase)Activator.CreateInstance(type, config)) - .FirstOrDefault(g => g!.IsSameUrl(url)) ?? - throw new ArgumentException($"Сайт {url.Host} не поддерживается"); - } -} diff --git a/src/Core/Types/AuthorToday/AuthorTodayBookDetails.cs b/src/Core/Types/AuthorToday/AuthorTodayBookDetails.cs deleted file mode 100644 index 03b27897..00000000 --- a/src/Core/Types/AuthorToday/AuthorTodayBookDetails.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.AuthorToday; - -public class AuthorTodayBookDetails -{ - [JsonPropertyName("annotation")] public string Annotation { get; set; } - - [JsonPropertyName("seriesId")] public long? SeriesId { get; set; } - - [JsonPropertyName("seriesWorkNumber")] public long? SeriesWorkNumber { get; set; } - - [JsonPropertyName("seriesTitle")] public string SeriesTitle { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("authorFIO")] public string AuthorFio { get; set; } - - [JsonPropertyName("coAuthorFIO")] public string CoAuthorFio { get; set; } - - [JsonPropertyName("coverUrl")] public string CoverUrl { get; set; } - - [JsonPropertyName("chapters")] public AuthorTodayChapter[] Chapters { get; set; } - - [JsonPropertyName("id")] public long Id { get; set; } - - [JsonPropertyName("authorUserName")] public string AuthorUserName { get; set; } - - [JsonPropertyName("CoAuthorUserName")] public string CoAuthorUserName { get; set; } - - [JsonPropertyName("galleryImages")] public AuthorTodayImage[] GalleryImages { get; set; } -} diff --git a/src/Core/Types/AuthorToday/AuthorTodayImage.cs b/src/Core/Types/AuthorToday/AuthorTodayImage.cs deleted file mode 100644 index a2c27ece..00000000 --- a/src/Core/Types/AuthorToday/AuthorTodayImage.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.AuthorToday; - -public class AuthorTodayImage -{ - [JsonPropertyName("url")] public string Url { get; set; } -} diff --git a/src/Core/Types/AuthorToday/AuthorTodayUser.cs b/src/Core/Types/AuthorToday/AuthorTodayUser.cs deleted file mode 100644 index 0c88c898..00000000 --- a/src/Core/Types/AuthorToday/AuthorTodayUser.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.AuthorToday; - -public class AuthorTodayUser -{ - [JsonPropertyName("id")] public long Id { get; set; } -} diff --git a/src/Core/Types/BookYandex/BooksYandexAudioBitrate.cs b/src/Core/Types/BookYandex/BooksYandexAudioBitrate.cs deleted file mode 100644 index 0dd88efc..00000000 --- a/src/Core/Types/BookYandex/BooksYandexAudioBitrate.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexAudioBitrate -{ - [JsonPropertyName("url")] public string Url { get; set; } -} diff --git a/src/Core/Types/BookYandex/BooksYandexAudioOffline.cs b/src/Core/Types/BookYandex/BooksYandexAudioOffline.cs deleted file mode 100644 index 1d08394b..00000000 --- a/src/Core/Types/BookYandex/BooksYandexAudioOffline.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexAudioOffline -{ - [JsonPropertyName("max_bit_rate")] public BooksYandexAudioBitrate Max { get; set; } - - [JsonPropertyName("min_bit_rate")] public BooksYandexAudioBitrate Min { get; set; } -} diff --git a/src/Core/Types/BookYandex/BooksYandexAudioTrack.cs b/src/Core/Types/BookYandex/BooksYandexAudioTrack.cs deleted file mode 100644 index 9c3bf6de..00000000 --- a/src/Core/Types/BookYandex/BooksYandexAudioTrack.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexAudioTrack -{ - [JsonPropertyName("offline")] public BooksYandexAudioOffline Offline { get; set; } -} diff --git a/src/Core/Types/BookYandex/BooksYandexAuthor.cs b/src/Core/Types/BookYandex/BooksYandexAuthor.cs deleted file mode 100644 index fdb745d9..00000000 --- a/src/Core/Types/BookYandex/BooksYandexAuthor.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexAuthor -{ - [JsonPropertyName("name")] public string Name { get; set; } - - [JsonPropertyName("uuid")] public string Uuid { get; set; } -} diff --git a/src/Core/Types/BookYandex/BooksYandexBook.cs b/src/Core/Types/BookYandex/BooksYandexBook.cs deleted file mode 100644 index 05301be4..00000000 --- a/src/Core/Types/BookYandex/BooksYandexBook.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public abstract class BookmateBookBase -{ - [JsonPropertyName("uuid")] public string UUID { get; set; } - - [JsonPropertyName("annotation")] public string Annotation { get; set; } - - [JsonPropertyName("cover")] public BooksYandexCover Cover { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("language")] public string Language { get; set; } - - [JsonPropertyName("linked_book_uuids")] public string[] LinkedBooks { get; set; } - - [JsonPropertyName("linked_audiobook_uuids")] public string[] LinkedAudio { get; set; } - - [JsonPropertyName("source_type")] public string SourceType { get; set; } - - public abstract BooksYandexAuthor GetAuthor(); -} - -public class BooksYandexBook : BookmateBookBase -{ - [JsonPropertyName("authors_objects")] public BooksYandexAuthor[] AuthorsObjects { get; set; } - - public override BooksYandexAuthor GetAuthor() - { - return AuthorsObjects?.FirstOrDefault(); - } -} - -public class BookmateComic : BookmateBookBase -{ - [JsonPropertyName("authors")] public BooksYandexAuthor[] Authors { get; set; } - - public override BooksYandexAuthor GetAuthor() - { - return Authors?.FirstOrDefault(); - } -} diff --git a/src/Core/Types/BookYandex/BooksYandexComicContent.cs b/src/Core/Types/BookYandex/BooksYandexComicContent.cs deleted file mode 100644 index cfb5126d..00000000 --- a/src/Core/Types/BookYandex/BooksYandexComicContent.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexComicContent -{ - [JsonPropertyName("uris")] public BooksYandexUri Uri { get; set; } -} diff --git a/src/Core/Types/BookYandex/BooksYandexComicMetadata.cs b/src/Core/Types/BookYandex/BooksYandexComicMetadata.cs deleted file mode 100644 index ca5ee961..00000000 --- a/src/Core/Types/BookYandex/BooksYandexComicMetadata.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexComicMetadata -{ - [JsonPropertyName("pages")] public BooksYandexComicPage[] Pages { get; set; } -} diff --git a/src/Core/Types/BookYandex/BooksYandexComicPage.cs b/src/Core/Types/BookYandex/BooksYandexComicPage.cs deleted file mode 100644 index 797c0c78..00000000 --- a/src/Core/Types/BookYandex/BooksYandexComicPage.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexComicPage -{ - [JsonPropertyName("content")] public BooksYandexComicContent Content { get; set; } -} diff --git a/src/Core/Types/BookYandex/BooksYandexCover.cs b/src/Core/Types/BookYandex/BooksYandexCover.cs deleted file mode 100644 index 5cf57455..00000000 --- a/src/Core/Types/BookYandex/BooksYandexCover.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexCover -{ - [JsonPropertyName("large")] public string Large { get; set; } - - [JsonPropertyName("small")] public string Small { get; set; } -} diff --git a/src/Core/Types/BookYandex/BooksYandexEpisode.cs b/src/Core/Types/BookYandex/BooksYandexEpisode.cs deleted file mode 100644 index e56ff4d3..00000000 --- a/src/Core/Types/BookYandex/BooksYandexEpisode.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexEpisode -{ - [JsonPropertyName("uuid")] public string Uuid { get; set; } - - [JsonPropertyName("can_be_read")] public bool CanBeRead { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } -} diff --git a/src/Core/Types/BookYandex/BooksYandexEpisodes.cs b/src/Core/Types/BookYandex/BooksYandexEpisodes.cs deleted file mode 100644 index 5457bbeb..00000000 --- a/src/Core/Types/BookYandex/BooksYandexEpisodes.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexEpisodes -{ - [JsonPropertyName("episodes")] public BooksYandexEpisode[] Episodes { get; set; } -} diff --git a/src/Core/Types/BookYandex/BooksYandexPlaylist.cs b/src/Core/Types/BookYandex/BooksYandexPlaylist.cs deleted file mode 100644 index b8c1416c..00000000 --- a/src/Core/Types/BookYandex/BooksYandexPlaylist.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexPlaylist -{ - [JsonPropertyName("tracks")] public BooksYandexAudioTrack[] Tracks { get; set; } -} diff --git a/src/Core/Types/BookYandex/BooksYandexResponse.cs b/src/Core/Types/BookYandex/BooksYandexResponse.cs deleted file mode 100644 index 4e0f14b3..00000000 --- a/src/Core/Types/BookYandex/BooksYandexResponse.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexResponse -{ - [JsonPropertyName("book")] public BooksYandexBook Book { get; set; } - - [JsonPropertyName("audiobook")] public BooksYandexBook AudioBook { get; set; } - - [JsonPropertyName("comicbook")] public BookmateComic Comicbook { get; set; } -} diff --git a/src/Core/Types/BookYandex/BooksYandexUri.cs b/src/Core/Types/BookYandex/BooksYandexUri.cs deleted file mode 100644 index 5ca243d3..00000000 --- a/src/Core/Types/BookYandex/BooksYandexUri.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.BookYandex; - -public class BooksYandexUri -{ - [JsonPropertyName("Image")] public string Image { get; set; } - - [JsonPropertyName("path")] public string Path { get; set; } -} diff --git a/src/Core/Types/Bookriver/BookRiverApiResponse.cs b/src/Core/Types/Bookriver/BookRiverApiResponse.cs deleted file mode 100644 index 7ab44b25..00000000 --- a/src/Core/Types/Bookriver/BookRiverApiResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookriver; - -public class BookRiverApiResponse -{ - [JsonPropertyName("data")] public T Data { get; set; } -} diff --git a/src/Core/Types/Bookriver/BookRiverAuthResponse.cs b/src/Core/Types/Bookriver/BookRiverAuthResponse.cs deleted file mode 100644 index 3e0b02e7..00000000 --- a/src/Core/Types/Bookriver/BookRiverAuthResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookriver; - -public class BookRiverAuthResponse -{ - [JsonPropertyName("token")] public string Token { get; set; } -} diff --git a/src/Core/Types/Bookriver/BookRiverBook.cs b/src/Core/Types/Bookriver/BookRiverBook.cs deleted file mode 100644 index 1c01f510..00000000 --- a/src/Core/Types/Bookriver/BookRiverBook.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookriver; - -public class BookRiverBook -{ - [JsonPropertyName("currentGraphqlBook")] public BookRiverCurrentBook Book { get; set; } -} diff --git a/src/Core/Types/Bookriver/BookRiverChapter.cs b/src/Core/Types/Bookriver/BookRiverChapter.cs deleted file mode 100644 index 735617ff..00000000 --- a/src/Core/Types/Bookriver/BookRiverChapter.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookriver; - -public class BookRiverChapter -{ - [JsonPropertyName("id")] public long Id { get; set; } - - [JsonPropertyName("name")] public string Name { get; set; } -} diff --git a/src/Core/Types/Bookriver/BookRiverChapterContent.cs b/src/Core/Types/Bookriver/BookRiverChapterContent.cs deleted file mode 100644 index 350aae05..00000000 --- a/src/Core/Types/Bookriver/BookRiverChapterContent.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookriver; - -public class BookRiverChapterContent -{ - [JsonPropertyName("content")] public string Content { get; set; } -} diff --git a/src/Core/Types/Bookriver/BookRiverCurrentBook.cs b/src/Core/Types/Bookriver/BookRiverCurrentBook.cs deleted file mode 100644 index 5c5d27ba..00000000 --- a/src/Core/Types/Bookriver/BookRiverCurrentBook.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookriver; - -public class BookRiverCurrentBook -{ - [JsonPropertyName("id")] public long Id { get; set; } -} diff --git a/src/Core/Types/Bookstab/BookstabApiResponse.cs b/src/Core/Types/Bookstab/BookstabApiResponse.cs deleted file mode 100644 index d6c77728..00000000 --- a/src/Core/Types/Bookstab/BookstabApiResponse.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookstab; - -public class BookstabApiResponse -{ - [JsonPropertyName("book")] public BookstabBook Book { get; set; } - - [JsonPropertyName("chapter")] public BookstabChapter Chapter { get; set; } -} diff --git a/src/Core/Types/Bookstab/BookstabBook.cs b/src/Core/Types/Bookstab/BookstabBook.cs deleted file mode 100644 index d01cc902..00000000 --- a/src/Core/Types/Bookstab/BookstabBook.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookstab; - -public class BookstabBook -{ - [JsonPropertyName("image")] public string Image { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("excerpt")] public string Excerpt { get; set; } - - [JsonPropertyName("user")] public BooksnabUser User { get; set; } - - [JsonPropertyName("chapters_show")] public BookstabChapter[] ChaptersShow { get; set; } -} diff --git a/src/Core/Types/Bookstab/BookstabChapter.cs b/src/Core/Types/Bookstab/BookstabChapter.cs deleted file mode 100644 index 3d455b5f..00000000 --- a/src/Core/Types/Bookstab/BookstabChapter.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookstab; - -public class BookstabChapter -{ - [JsonPropertyName("id")] public int Id { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("body")] public string Body { get; set; } -} diff --git a/src/Core/Types/Bookstab/BookstabUser.cs b/src/Core/Types/Bookstab/BookstabUser.cs deleted file mode 100644 index ef62bd8c..00000000 --- a/src/Core/Types/Bookstab/BookstabUser.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookstab; - -public class BooksnabUser -{ - [JsonPropertyName("pseudonym")] public string Pseudonym { get; set; } - - [JsonPropertyName("name")] public string Name { get; set; } -} diff --git a/src/Core/Types/Bookstime/BookstimeAuthResponse.cs b/src/Core/Types/Bookstime/BookstimeAuthResponse.cs deleted file mode 100644 index 7b342e21..00000000 --- a/src/Core/Types/Bookstime/BookstimeAuthResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Bookstime; - -public class BookstimeAuthResponse -{ - [JsonPropertyName("X_OCTOBER_ERROR_MESSAGE")] public string XOctoberErrorMessage { get; set; } -} diff --git a/src/Core/Types/Boosty/BoostyApiResponse.cs b/src/Core/Types/Boosty/BoostyApiResponse.cs deleted file mode 100644 index db5f6ba3..00000000 --- a/src/Core/Types/Boosty/BoostyApiResponse.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Boosty; - -public class BoostyApiResponse -{ - [JsonPropertyName("data")] public T Data { get; set; } - - [JsonPropertyName("extra")] public BoostyExtra Extra { get; set; } -} diff --git a/src/Core/Types/Boosty/BoostyExtra.cs b/src/Core/Types/Boosty/BoostyExtra.cs deleted file mode 100644 index d33cee9c..00000000 --- a/src/Core/Types/Boosty/BoostyExtra.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Boosty; - -public class BoostyExtra -{ - [JsonPropertyName("isLast")] public bool IsLast { get; set; } - - [JsonPropertyName("offset")] public string Offset { get; set; } -} diff --git a/src/Core/Types/Boosty/BoostyPost.cs b/src/Core/Types/Boosty/BoostyPost.cs deleted file mode 100644 index a203fdc3..00000000 --- a/src/Core/Types/Boosty/BoostyPost.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Boosty; - -public class BoostyPost -{ - [JsonPropertyName("id")] public Guid Id { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("hasAccess")] public bool HasAccess { get; set; } -} diff --git a/src/Core/Types/DarkNovels/DarkNovelsAuthData.cs b/src/Core/Types/DarkNovels/DarkNovelsAuthData.cs deleted file mode 100644 index fb11e03a..00000000 --- a/src/Core/Types/DarkNovels/DarkNovelsAuthData.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.DarkNovels; - -public class DarkNovelsAuthResponse -{ - [JsonPropertyName("token")] public DarkNovelsAuthToken Token { get; set; } -} diff --git a/src/Core/Types/DarkNovels/DarkNovelsAuthToken.cs b/src/Core/Types/DarkNovels/DarkNovelsAuthToken.cs deleted file mode 100644 index f43ac4ca..00000000 --- a/src/Core/Types/DarkNovels/DarkNovelsAuthToken.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.DarkNovels; - -public class DarkNovelsAuthToken -{ - [JsonPropertyName("accessToken")] public string AccessToken { get; set; } -} diff --git a/src/Core/Types/DarkNovels/DarkNovelsChapter.cs b/src/Core/Types/DarkNovels/DarkNovelsChapter.cs deleted file mode 100644 index ebd9af05..00000000 --- a/src/Core/Types/DarkNovels/DarkNovelsChapter.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.DarkNovels; - -public class DarkNovelsChapter -{ - [JsonPropertyName("id")] public int Id { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("payed")] public int Payed { get; set; } -} diff --git a/src/Core/Types/DarkNovels/DarkNovelsData.cs b/src/Core/Types/DarkNovels/DarkNovelsData.cs deleted file mode 100644 index dc2e59eb..00000000 --- a/src/Core/Types/DarkNovels/DarkNovelsData.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.DarkNovels; - -public class DarkNovelsData -{ - [JsonPropertyName("status")] public string Status { get; set; } - - [JsonPropertyName("message")] public string Message { get; set; } - - [JsonPropertyName("data")] public T Data { get; set; } -} diff --git a/src/Core/Types/Dreame/DreameApiResponse.cs b/src/Core/Types/Dreame/DreameApiResponse.cs deleted file mode 100644 index 92a787b1..00000000 --- a/src/Core/Types/Dreame/DreameApiResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Dreame; - -public class DreameApiResponse -{ - [JsonPropertyName("data")] public T Data { get; set; } -} diff --git a/src/Core/Types/Dreame/DreameCatalog.cs b/src/Core/Types/Dreame/DreameCatalog.cs deleted file mode 100644 index c0f73a84..00000000 --- a/src/Core/Types/Dreame/DreameCatalog.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Dreame; - -public class DreameCatalog -{ - [JsonPropertyName("pager")] public DreamePager Pager { get; set; } -} diff --git a/src/Core/Types/Dreame/DreameChapter.cs b/src/Core/Types/Dreame/DreameChapter.cs deleted file mode 100644 index 06b06dd3..00000000 --- a/src/Core/Types/Dreame/DreameChapter.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Dreame; - -public class DreameChapter -{ - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("id")] public string Id { get; set; } - - [JsonPropertyName("content")] public string Content { get; set; } -} diff --git a/src/Core/Types/Dreame/DreameNovel.cs b/src/Core/Types/Dreame/DreameNovel.cs deleted file mode 100644 index 3062b18e..00000000 --- a/src/Core/Types/Dreame/DreameNovel.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Dreame; - -public class DreameNovel -{ - [JsonPropertyName("id")] public long Id { get; set; } - - [JsonPropertyName("author_id")] public string AuthorId { get; set; } - - [JsonPropertyName("author_name")] public string AuthorName { get; set; } - - [JsonPropertyName("name")] public string Name { get; set; } - - [JsonPropertyName("descr")] public string Description { get; set; } - - [JsonPropertyName("language")] public string Language { get; set; } - - [JsonPropertyName("cover_url")] public string Cover { get; set; } -} diff --git a/src/Core/Types/Dreame/DreamePager.cs b/src/Core/Types/Dreame/DreamePager.cs deleted file mode 100644 index 021a1b2d..00000000 --- a/src/Core/Types/Dreame/DreamePager.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Dreame; - -public class DreamePager -{ - [JsonPropertyName("chap_list")] public List ChapterList { get; set; } -} diff --git a/src/Core/Types/FanFicus/FanFicusApiResponse.cs b/src/Core/Types/FanFicus/FanFicusApiResponse.cs deleted file mode 100644 index e668d16c..00000000 --- a/src/Core/Types/FanFicus/FanFicusApiResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.FanFicus; - -public class FanFicusApiResponse -{ - [JsonPropertyName("value")] public T Value { get; set; } -} diff --git a/src/Core/Types/FanFicus/FanFicusBook.cs b/src/Core/Types/FanFicus/FanFicusBook.cs deleted file mode 100644 index d2da3f03..00000000 --- a/src/Core/Types/FanFicus/FanFicusBook.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.FanFicus; - -public class FanFicusBook -{ - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("description")] public string Description { get; set; } - - [JsonPropertyName("creatorId")] public FanFicusCreator[] Creators { get; set; } - - [JsonPropertyName("images")] public FanFicusImage[] Images { get; set; } -} diff --git a/src/Core/Types/FanFicus/FanFicusChapter.cs b/src/Core/Types/FanFicus/FanFicusChapter.cs deleted file mode 100644 index ca2c2f78..00000000 --- a/src/Core/Types/FanFicus/FanFicusChapter.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.FanFicus; - -public class FanFicusChapter -{ - [JsonPropertyName("text")] public string Text { get; set; } -} diff --git a/src/Core/Types/FanFicus/FanFicusCreator.cs b/src/Core/Types/FanFicus/FanFicusCreator.cs deleted file mode 100644 index 8ea2962c..00000000 --- a/src/Core/Types/FanFicus/FanFicusCreator.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.FanFicus; - -public class FanFicusCreator -{ - [JsonPropertyName("_id")] public string Id { get; set; } - - [JsonPropertyName("nickName")] public string NickName { get; set; } -} diff --git a/src/Core/Types/FanFicus/FanFicusImage.cs b/src/Core/Types/FanFicus/FanFicusImage.cs deleted file mode 100644 index b42d04e2..00000000 --- a/src/Core/Types/FanFicus/FanFicusImage.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.FanFicus; - -public class FanFicusImage -{ - [JsonPropertyName("url")] public string Url { get; set; } -} diff --git a/src/Core/Types/FanFicus/FanFicusPart.cs b/src/Core/Types/FanFicus/FanFicusPart.cs deleted file mode 100644 index e553d17f..00000000 --- a/src/Core/Types/FanFicus/FanFicusPart.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.FanFicus; - -public class FanFicusPart -{ - [JsonPropertyName("_id")] public string Id { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } -} diff --git a/src/Core/Types/FanFicus/FanFicusUser.cs b/src/Core/Types/FanFicus/FanFicusUser.cs deleted file mode 100644 index 8eff5e7e..00000000 --- a/src/Core/Types/FanFicus/FanFicusUser.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.FanFicus; - -public class FanFicusUser -{ - [JsonPropertyName("token")] public string Token { get; set; } -} diff --git a/src/Core/Types/Freedlit/FreedlitApiResponse.cs b/src/Core/Types/Freedlit/FreedlitApiResponse.cs deleted file mode 100644 index 4c849db6..00000000 --- a/src/Core/Types/Freedlit/FreedlitApiResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitApiResponse -{ - [JsonPropertyName("success")] public T Success { get; set; } -} diff --git a/src/Core/Types/Freedlit/FreedlitApp.cs b/src/Core/Types/Freedlit/FreedlitApp.cs deleted file mode 100644 index 4ad1add7..00000000 --- a/src/Core/Types/Freedlit/FreedlitApp.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitApp -{ - [JsonPropertyName("props")] public FreedlitProps Props { get; set; } -} diff --git a/src/Core/Types/Freedlit/FreedlitAuthError.cs b/src/Core/Types/Freedlit/FreedlitAuthError.cs deleted file mode 100644 index 62d91adc..00000000 --- a/src/Core/Types/Freedlit/FreedlitAuthError.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitAuthError -{ - [JsonPropertyName("email")] public string Email { get; set; } - - [JsonPropertyName("password")] public string[] Password { get; set; } -} diff --git a/src/Core/Types/Freedlit/FreedlitAuthor.cs b/src/Core/Types/Freedlit/FreedlitAuthor.cs deleted file mode 100644 index a5316187..00000000 --- a/src/Core/Types/Freedlit/FreedlitAuthor.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitAuthor -{ - [JsonPropertyName("id")] public long Id { get; set; } - - [JsonPropertyName("user_link")] public string UserLink { get; set; } - - [JsonPropertyName("name")] public string Name { get; set; } -} diff --git a/src/Core/Types/Freedlit/FreedlitBook.cs b/src/Core/Types/Freedlit/FreedlitBook.cs deleted file mode 100644 index bef06ad7..00000000 --- a/src/Core/Types/Freedlit/FreedlitBook.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitBook -{ - [JsonPropertyName("id")] public long Id { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("cover")] public string Cover { get; set; } - - [JsonPropertyName("annotation")] public string Annotation { get; set; } - - [JsonPropertyName("language")] public string Language { get; set; } - - [JsonPropertyName("main_author")] public FreedlitAuthor MainAuthor { get; set; } -} diff --git a/src/Core/Types/Freedlit/FreedlitChapter.cs b/src/Core/Types/Freedlit/FreedlitChapter.cs deleted file mode 100644 index 63c1de48..00000000 --- a/src/Core/Types/Freedlit/FreedlitChapter.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitChapter -{ - [JsonPropertyName("id")] public long Id { get; set; } - - [JsonPropertyName("header")] public string Header { get; set; } - - [JsonPropertyName("content")] public string Content { get; set; } -} diff --git a/src/Core/Types/Freedlit/FreedlitItemsContent.cs b/src/Core/Types/Freedlit/FreedlitItemsContent.cs deleted file mode 100644 index dc2151b3..00000000 --- a/src/Core/Types/Freedlit/FreedlitItemsContent.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitItemsContent -{ - [JsonPropertyName("items")] public List Items { get; set; } -} diff --git a/src/Core/Types/Freedlit/FreedlitProps.cs b/src/Core/Types/Freedlit/FreedlitProps.cs deleted file mode 100644 index d9a84d5e..00000000 --- a/src/Core/Types/Freedlit/FreedlitProps.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitProps -{ - [JsonPropertyName("book")] public FreedlitBook Book { get; set; } - - [JsonPropertyName("errors")] public TErr Errors { get; set; } -} diff --git a/src/Core/Types/Freedlit/FreedlitSuccessResponse.cs b/src/Core/Types/Freedlit/FreedlitSuccessResponse.cs deleted file mode 100644 index d7069700..00000000 --- a/src/Core/Types/Freedlit/FreedlitSuccessResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Freedlit; - -public class FreedlitSuccessResponse -{ - [JsonPropertyName("items")] public List Items { get; set; } -} diff --git a/src/Core/Types/HotNovelPub/HotNovelPubApiResponse.cs b/src/Core/Types/HotNovelPub/HotNovelPubApiResponse.cs deleted file mode 100644 index 1c14bb4e..00000000 --- a/src/Core/Types/HotNovelPub/HotNovelPubApiResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.HotNovelPub; - -public class HotNovelPubApiResponse -{ - [JsonPropertyName("data")] public T Data { get; set; } -} diff --git a/src/Core/Types/HotNovelPub/HotNovelPubAuthorize.cs b/src/Core/Types/HotNovelPub/HotNovelPubAuthorize.cs deleted file mode 100644 index 19df3a4b..00000000 --- a/src/Core/Types/HotNovelPub/HotNovelPubAuthorize.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.HotNovelPub; - -public class HotNovelPubAuthorize -{ - [JsonPropertyName("id")] public int Id { get; set; } - - [JsonPropertyName("name")] public string Name { get; set; } - - [JsonPropertyName("slug")] public string Slug { get; set; } -} diff --git a/src/Core/Types/HotNovelPub/HotNovelPubBook.cs b/src/Core/Types/HotNovelPub/HotNovelPubBook.cs deleted file mode 100644 index f26d2765..00000000 --- a/src/Core/Types/HotNovelPub/HotNovelPubBook.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.HotNovelPub; - -public class HotNovelPubBook -{ - [JsonPropertyName("name")] public string Name { get; set; } - - [JsonPropertyName("description")] public string Description { get; set; } - - [JsonPropertyName("authorize")] public HotNovelPubAuthorize Authorize { get; set; } - - [JsonPropertyName("image")] public string Image { get; set; } -} diff --git a/src/Core/Types/HotNovelPub/HotNovelPubBookResponse.cs b/src/Core/Types/HotNovelPub/HotNovelPubBookResponse.cs deleted file mode 100644 index 211e3e25..00000000 --- a/src/Core/Types/HotNovelPub/HotNovelPubBookResponse.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.HotNovelPub; - -public class HotNovelPubBookResponse -{ - [JsonPropertyName("book")] public HotNovelPubBook Book { get; set; } - - [JsonPropertyName("chapters")] public List Chapters { get; set; } -} diff --git a/src/Core/Types/HotNovelPub/HotNovelPubChapter.cs b/src/Core/Types/HotNovelPub/HotNovelPubChapter.cs deleted file mode 100644 index 211e4bc9..00000000 --- a/src/Core/Types/HotNovelPub/HotNovelPubChapter.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.HotNovelPub; - -public class HotNovelPubChapter -{ - [JsonPropertyName("id")] public int Id { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("slug")] public string Slug { get; set; } -} diff --git a/src/Core/Types/Librebook/LibrebookAuthResponse.cs b/src/Core/Types/Librebook/LibrebookAuthResponse.cs deleted file mode 100644 index f4984ede..00000000 --- a/src/Core/Types/Librebook/LibrebookAuthResponse.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Librebook; - -public class LibrebookAuthResponse -{ - [JsonPropertyName("type")] public string Type { get; set; } - - [JsonPropertyName("text")] public string Text { get; set; } -} diff --git a/src/Core/Types/Litexit/LitexitChapter.cs b/src/Core/Types/Litexit/LitexitChapter.cs deleted file mode 100644 index 9d38d3b3..00000000 --- a/src/Core/Types/Litexit/LitexitChapter.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litexit; - -public class LitexitChapter -{ - [JsonPropertyName("id")] public int Id { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("text")] public string Text { get; set; } -} diff --git a/src/Core/Types/Litexit/LitexitUser.cs b/src/Core/Types/Litexit/LitexitUser.cs deleted file mode 100644 index b4a4706f..00000000 --- a/src/Core/Types/Litexit/LitexitUser.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litexit; - -public class LitexitUser -{ - [JsonPropertyName("id")] public int Id { get; set; } -} diff --git a/src/Core/Types/Litgorod/LitgorodAuthResponse.cs b/src/Core/Types/Litgorod/LitgorodAuthResponse.cs deleted file mode 100644 index 9be9dabc..00000000 --- a/src/Core/Types/Litgorod/LitgorodAuthResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litgorod; - -public class LitgorodAuthResponse -{ - [JsonPropertyName("message")] public string Message { get; set; } -} diff --git a/src/Core/Types/Litgorod/LitgorodBookPart.cs b/src/Core/Types/Litgorod/LitgorodBookPart.cs deleted file mode 100644 index 873c82a1..00000000 --- a/src/Core/Types/Litgorod/LitgorodBookPart.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litgorod; - -public class LitgorodBookPart -{ - [JsonPropertyName("text")] public string Text { get; set; } -} diff --git a/src/Core/Types/Litgorod/LitgorodChapter.cs b/src/Core/Types/Litgorod/LitgorodChapter.cs deleted file mode 100644 index 7cbca461..00000000 --- a/src/Core/Types/Litgorod/LitgorodChapter.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litgorod; - -public class LitgorodChapter -{ - [JsonPropertyName("explodedParagraphs")] public string[] Paragraphs { get; set; } -} diff --git a/src/Core/Types/Litmarket/AuthResponse.cs b/src/Core/Types/Litmarket/AuthResponse.cs deleted file mode 100644 index ad28974b..00000000 --- a/src/Core/Types/Litmarket/AuthResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litmarket; - -public class AuthResponse -{ - [JsonPropertyName("success")] public bool Success { get; set; } -} diff --git a/src/Core/Types/Litmarket/Block.cs b/src/Core/Types/Litmarket/Block.cs deleted file mode 100644 index 0d07077e..00000000 --- a/src/Core/Types/Litmarket/Block.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litmarket; - -public class Block -{ - [JsonPropertyName("chunk")] public Chunk Chunk { get; set; } - - [JsonPropertyName("index")] public int Index { get; set; } -} diff --git a/src/Core/Types/Litmarket/Chunk.cs b/src/Core/Types/Litmarket/Chunk.cs deleted file mode 100644 index 0b825cbd..00000000 --- a/src/Core/Types/Litmarket/Chunk.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litmarket; - -public class Chunk -{ - [JsonPropertyName("mods")] public Mod[] Mods { get; set; } -} diff --git a/src/Core/Types/Litmarket/LBook.cs b/src/Core/Types/Litmarket/LBook.cs deleted file mode 100644 index 689f5f57..00000000 --- a/src/Core/Types/Litmarket/LBook.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litmarket; - -public class LBook -{ - [JsonPropertyName("ebookId")] public int EbookId { get; set; } -} diff --git a/src/Core/Types/Litmarket/Mod.cs b/src/Core/Types/Litmarket/Mod.cs deleted file mode 100644 index 29572b1b..00000000 --- a/src/Core/Types/Litmarket/Mod.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Core.Types.Litmarket; - -public class Mod -{ - [JsonPropertyName("type")] public string Type { get; set; } - - [JsonPropertyName("styles")] public string[] Styles { get; set; } - - [JsonPropertyName("text")] public string Text { get; set; } - - [JsonPropertyName("data")] public JsonElement Data { get; set; } - - [JsonPropertyName("mods")] public Mod[] Mods { get; set; } -} diff --git a/src/Core/Types/Litmarket/Response.cs b/src/Core/Types/Litmarket/Response.cs deleted file mode 100644 index fe518b08..00000000 --- a/src/Core/Types/Litmarket/Response.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litmarket; - -public class Response -{ - [JsonPropertyName("book")] public LBook Book { get; set; } - - [JsonPropertyName("tableOfContent")] public string Toc { get; set; } -} diff --git a/src/Core/Types/Litnet/LitnetAuthResponse.cs b/src/Core/Types/Litnet/LitnetAuthResponse.cs deleted file mode 100644 index fb66d4b8..00000000 --- a/src/Core/Types/Litnet/LitnetAuthResponse.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litnet; - -public class LitnetAuthResponse -{ - [JsonPropertyName("error")] public string Error { get; set; } - - [JsonPropertyName("token")] public string Token { get; set; } -} diff --git a/src/Core/Types/Litnet/LitnetBookResponse.cs b/src/Core/Types/Litnet/LitnetBookResponse.cs deleted file mode 100644 index b341a2f5..00000000 --- a/src/Core/Types/Litnet/LitnetBookResponse.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litnet; - -public class LitnetBookResponse -{ - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("author_name")] public string AuthorName { get; set; } - - [JsonPropertyName("cover")] public string Cover { get; set; } - - [JsonPropertyName("annotation")] public string Annotation { get; set; } - - [JsonPropertyName("url")] public string Url { get; set; } - - [JsonPropertyName("author_id")] public long AuthorId { get; set; } - - [JsonPropertyName("lang")] public string Lang { get; set; } - - [JsonPropertyName("cycle_priority")] public int? CyclePriority { get; set; } - - [JsonPropertyName("adult_only")] public bool AdultOnly { get; set; } - - [JsonPropertyName("co_author_name")] public string CoAuthorName { get; set; } - - [JsonPropertyName("co_author")] public long? CoAuthorId { get; set; } -} diff --git a/src/Core/Types/Litnet/LitnetChapterResponse.cs b/src/Core/Types/Litnet/LitnetChapterResponse.cs deleted file mode 100644 index d88052db..00000000 --- a/src/Core/Types/Litnet/LitnetChapterResponse.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litnet; - -public class LitnetChapterResponse -{ - [JsonPropertyName("id")] public long Id { get; set; } - - [JsonPropertyName("text")] public string Text { get; set; } -} diff --git a/src/Core/Types/Litnet/LitnetContentsResponse.cs b/src/Core/Types/Litnet/LitnetContentsResponse.cs deleted file mode 100644 index d7e381f8..00000000 --- a/src/Core/Types/Litnet/LitnetContentsResponse.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litnet; - -public class LitnetContentsResponse -{ - [JsonPropertyName("id")] public long Id { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } -} diff --git a/src/Core/Types/Litres/Requests/LitresAuthData.cs b/src/Core/Types/Litres/Requests/LitresAuthData.cs deleted file mode 100644 index e19fb8eb..00000000 --- a/src/Core/Types/Litres/Requests/LitresAuthData.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Requests; - -public class LitresAuthData -{ - [JsonPropertyName("login")] public string Login { get; set; } - - [JsonPropertyName("pwd")] public string Password { get; set; } -} diff --git a/src/Core/Types/Litres/Requests/LitresBrowseArtsData.cs b/src/Core/Types/Litres/Requests/LitresBrowseArtsData.cs deleted file mode 100644 index e4540326..00000000 --- a/src/Core/Types/Litres/Requests/LitresBrowseArtsData.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Requests; - -public class LitresBrowseArtsData -{ - [JsonPropertyName("currency")] public string Currency { get; set; } - - [JsonPropertyName("anno")] public string Anno { get; set; } - - [JsonPropertyName("id")] public string[] Id { get; set; } -} diff --git a/src/Core/Types/Litres/Requests/LitresRequestBase.cs b/src/Core/Types/Litres/Requests/LitresRequestBase.cs deleted file mode 100644 index 81ec054c..00000000 --- a/src/Core/Types/Litres/Requests/LitresRequestBase.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Requests; - -public abstract class LitresRequestBase where T : new() -{ - [JsonPropertyName("id")] public string Id { get; set; } = "data"; - - [JsonPropertyName("func")] public string Func { get; set; } - - [JsonPropertyName("param")] public T Param { get; } = new(); -} diff --git a/src/Core/Types/Litres/Response/LitresArt.cs b/src/Core/Types/Litres/Response/LitresArt.cs deleted file mode 100644 index d9c51e47..00000000 --- a/src/Core/Types/Litres/Response/LitresArt.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresArt -{ - [JsonPropertyName("id")] public long Id { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("html_annotation")] public string Annotation { get; set; } - - [JsonPropertyName("persons")] public LitresPerson[] Persons { get; set; } = []; - - [JsonPropertyName("cover_url")] public string Cover { get; set; } - - [JsonPropertyName("series")] public LitresSeria[] Sequences { get; set; } = []; - - [JsonPropertyName("linked_arts")] public LitresArt[] LinkedArts { get; set; } - - [JsonPropertyName("art_type")] public LitresArtTypeEnum ArtType { get; set; } -} diff --git a/src/Core/Types/Litres/Response/LitresArtTypeEnum.cs b/src/Core/Types/Litres/Response/LitresArtTypeEnum.cs deleted file mode 100644 index ea5ddd0b..00000000 --- a/src/Core/Types/Litres/Response/LitresArtTypeEnum.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Core.Types.Litres.Response; - -public enum LitresArtTypeEnum -{ - Text = 0, - Audio = 1, - Pdf = 4 -} diff --git a/src/Core/Types/Litres/Response/LitresAuthResponseData.cs b/src/Core/Types/Litres/Response/LitresAuthResponseData.cs deleted file mode 100644 index 77c9ef5f..00000000 --- a/src/Core/Types/Litres/Response/LitresAuthResponseData.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresAuthResponseData -{ - [JsonPropertyName("success")] public bool Success { get; set; } - - [JsonPropertyName("sid")] public string Sid { get; set; } - - [JsonPropertyName("error_message")] public string ErrorMessage { get; set; } -} diff --git a/src/Core/Types/Litres/Response/LitresFile.cs b/src/Core/Types/Litres/Response/LitresFile.cs deleted file mode 100644 index 7f0c54c5..00000000 --- a/src/Core/Types/Litres/Response/LitresFile.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresFile -{ - [JsonPropertyName("extension")] public string Extension { get; set; } - - [JsonPropertyName("id")] public long Id { get; set; } -} diff --git a/src/Core/Types/Litres/Response/LitresFiles.cs b/src/Core/Types/Litres/Response/LitresFiles.cs deleted file mode 100644 index 97b99f60..00000000 --- a/src/Core/Types/Litres/Response/LitresFiles.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresFiles -{ - [JsonPropertyName("files")] public LitresFile[] Files { get; set; } -} diff --git a/src/Core/Types/Litres/Response/LitresMe.cs b/src/Core/Types/Litres/Response/LitresMe.cs deleted file mode 100644 index cfce9205..00000000 --- a/src/Core/Types/Litres/Response/LitresMe.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresMe -{ - [JsonPropertyName("partner_subscriptions")] public LitresPartnerSubscriptions PartnerSubscriptions { get; set; } -} diff --git a/src/Core/Types/Litres/Response/LitresPartnerSubscriptions.cs b/src/Core/Types/Litres/Response/LitresPartnerSubscriptions.cs deleted file mode 100644 index 1d665297..00000000 --- a/src/Core/Types/Litres/Response/LitresPartnerSubscriptions.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresPartnerSubscriptions -{ - [JsonPropertyName("subscriptions")] public LitresSubscription[] Subscriptions { get; set; } -} diff --git a/src/Core/Types/Litres/Response/LitresPerson.cs b/src/Core/Types/Litres/Response/LitresPerson.cs deleted file mode 100644 index 48aa9fdd..00000000 --- a/src/Core/Types/Litres/Response/LitresPerson.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresPerson -{ - [JsonPropertyName("id")] public TId Id { get; set; } - - [JsonPropertyName("full_name")] public string FullName { get; set; } - - [JsonPropertyName("role")] public string Role { get; set; } - - [JsonPropertyName("url")] public string Url { get; set; } -} diff --git a/src/Core/Types/Litres/Response/LitresResponse.cs b/src/Core/Types/Litres/Response/LitresResponse.cs deleted file mode 100644 index e837d3ad..00000000 --- a/src/Core/Types/Litres/Response/LitresResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresResponse -{ - [JsonPropertyName("data")] public T Data { get; set; } -} diff --git a/src/Core/Types/Litres/Response/LitresSeria.cs b/src/Core/Types/Litres/Response/LitresSeria.cs deleted file mode 100644 index 81e1a82c..00000000 --- a/src/Core/Types/Litres/Response/LitresSeria.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresSeria -{ - [JsonPropertyName("name")] public string Name { get; set; } - - [JsonPropertyName("id")] public long Id { get; set; } - - [JsonPropertyName("sequence_number")] public string SequenceNumber { get; set; } -} diff --git a/src/Core/Types/Litres/Response/LitresStaticResponse.cs b/src/Core/Types/Litres/Response/LitresStaticResponse.cs deleted file mode 100644 index bebc55b9..00000000 --- a/src/Core/Types/Litres/Response/LitresStaticResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresStaticResponse -{ - [JsonPropertyName("payload")] public LitresResponse Payload { get; set; } -} diff --git a/src/Core/Types/Litres/Response/LitresSubscription.cs b/src/Core/Types/Litres/Response/LitresSubscription.cs deleted file mode 100644 index c1a4894a..00000000 --- a/src/Core/Types/Litres/Response/LitresSubscription.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litres.Response; - -public class LitresSubscription -{ - [JsonPropertyName("is_active")] public bool IsActive { get; set; } - - [JsonPropertyName("host")] public string Host { get; set; } - - [JsonPropertyName("type")] public string Type { get; set; } -} diff --git a/src/Core/Types/Litsovet/LitsovetLoginResponse.cs b/src/Core/Types/Litsovet/LitsovetLoginResponse.cs deleted file mode 100644 index 9ce32185..00000000 --- a/src/Core/Types/Litsovet/LitsovetLoginResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Litsovet; - -public class LitsovetLoginResponse -{ - [JsonPropertyName("ok")] public int Ok { get; set; } -} diff --git a/src/Core/Types/MangaLib/MangaLibPg.cs b/src/Core/Types/MangaLib/MangaLibPg.cs deleted file mode 100644 index 7bf9f03c..00000000 --- a/src/Core/Types/MangaLib/MangaLibPg.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.MangaLib; - -public class MangaLibPg -{ - [JsonPropertyName("p")] public int P { get; set; } - - [JsonPropertyName("u")] public string U { get; set; } -} diff --git a/src/Core/Types/MyBook/MyBookAuth.cs b/src/Core/Types/MyBook/MyBookAuth.cs deleted file mode 100644 index 6c193e58..00000000 --- a/src/Core/Types/MyBook/MyBookAuth.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.MyBook; - -public class MyBookAuth -{ - [JsonPropertyName("token")] public string Token { get; set; } - - [JsonPropertyName("secret")] public string Secret { get; set; } -} diff --git a/src/Core/Types/MyBook/MyBookAuthor.cs b/src/Core/Types/MyBook/MyBookAuthor.cs deleted file mode 100644 index 8fcae065..00000000 --- a/src/Core/Types/MyBook/MyBookAuthor.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.MyBook; - -public class MyBookAuthor -{ - [JsonPropertyName("cover_name")] public string Name { get; set; } - - [JsonPropertyName("absolute_url")] public string Url { get; set; } -} diff --git a/src/Core/Types/MyBook/MyBookBook.cs b/src/Core/Types/MyBook/MyBookBook.cs deleted file mode 100644 index edd5eb80..00000000 --- a/src/Core/Types/MyBook/MyBookBook.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -namespace Core.Types.MyBook; - -public class MyBookBook -{ - [JsonPropertyName("id")] public long Id { get; set; } - - [JsonPropertyName("annotation")] public string Annotation { get; set; } - - [JsonPropertyName("bookfile")] public string BookFile { get; set; } - - [JsonPropertyName("default_cover")] public string Cover { get; set; } - - [JsonPropertyName("name")] public string Name { get; set; } - - [JsonPropertyName("authors")] public MyBookAuthor[] Authors { get; set; } - - [JsonPropertyName("type")] public string Type { get; set; } - - [JsonPropertyName("mapfiles")] public MyBookMapFile[] MapFiles { get; set; } - - [JsonPropertyName("connected_book")] public MyBookBook Connected { get; set; } - - [JsonPropertyName("bookfiles")] public JsonNode[] Files { get; set; } -} diff --git a/src/Core/Types/MyBook/MyBookFile.cs b/src/Core/Types/MyBook/MyBookFile.cs deleted file mode 100644 index ddc72f88..00000000 --- a/src/Core/Types/MyBook/MyBookFile.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.MyBook; - -public class MyBookFile -{ - [JsonPropertyName("url")] public string Url { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("order")] public int Order { get; set; } -} diff --git a/src/Core/Types/MyBook/MyBookMapFile.cs b/src/Core/Types/MyBook/MyBookMapFile.cs deleted file mode 100644 index 2c4c6656..00000000 --- a/src/Core/Types/MyBook/MyBookMapFile.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.MyBook; - -public class MyBookMapFile -{ - [JsonPropertyName("book")] public long Book { get; set; } -} diff --git a/src/Core/Types/Neobook/NeobookAttachment.cs b/src/Core/Types/Neobook/NeobookAttachment.cs deleted file mode 100644 index 1f43fb55..00000000 --- a/src/Core/Types/Neobook/NeobookAttachment.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookAttachment -{ - [JsonPropertyName("image")] public Dictionary Cover { get; set; } -} diff --git a/src/Core/Types/Neobook/NeobookAuth.cs b/src/Core/Types/Neobook/NeobookAuth.cs deleted file mode 100644 index aa425870..00000000 --- a/src/Core/Types/Neobook/NeobookAuth.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookAuth -{ - [JsonPropertyName("error")] public string Error { get; set; } - - [JsonPropertyName("login")] public NeobookLogin Login { get; set; } -} diff --git a/src/Core/Types/Neobook/NeobookBook.cs b/src/Core/Types/Neobook/NeobookBook.cs deleted file mode 100644 index 3ea58aad..00000000 --- a/src/Core/Types/Neobook/NeobookBook.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookBook -{ - [JsonPropertyName("chapters")] public NeobookChapter[] Chapters { get; set; } - - [JsonPropertyName("active_chapter_index")] public int ActiveChapterIndex { get; set; } -} diff --git a/src/Core/Types/Neobook/NeobookChapter.cs b/src/Core/Types/Neobook/NeobookChapter.cs deleted file mode 100644 index 9e391174..00000000 --- a/src/Core/Types/Neobook/NeobookChapter.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookChapter -{ - [JsonPropertyName("data")] public NeobookChapterData Data { get; set; } -} diff --git a/src/Core/Types/Neobook/NeobookChapterData.cs b/src/Core/Types/Neobook/NeobookChapterData.cs deleted file mode 100644 index 85e39cdf..00000000 --- a/src/Core/Types/Neobook/NeobookChapterData.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookChapterData -{ - [JsonPropertyName("html")] public string Html { get; set; } -} diff --git a/src/Core/Types/Neobook/NeobookLogin.cs b/src/Core/Types/Neobook/NeobookLogin.cs deleted file mode 100644 index 1d2eaffb..00000000 --- a/src/Core/Types/Neobook/NeobookLogin.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookLogin -{ - [JsonPropertyName("utoken")] public string Utoken { get; set; } - - [JsonPropertyName("uid")] public string Uid { get; set; } -} diff --git a/src/Core/Types/Neobook/NeobookPostData.cs b/src/Core/Types/Neobook/NeobookPostData.cs deleted file mode 100644 index fb6c85f3..00000000 --- a/src/Core/Types/Neobook/NeobookPostData.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookPostData -{ - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("text")] public string Text { get; set; } - - [JsonPropertyName("user")] public NeobookUser User { get; set; } - - [JsonPropertyName("chapters")] public NeobookTocChapter[] Chapters { get; set; } - - [JsonPropertyName("attachment")] public NeobookAttachment Attachment { get; set; } -} diff --git a/src/Core/Types/Neobook/NeobookTocChapter.cs b/src/Core/Types/Neobook/NeobookTocChapter.cs deleted file mode 100644 index e1540f65..00000000 --- a/src/Core/Types/Neobook/NeobookTocChapter.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookTocChapter -{ - [JsonPropertyName("token")] public string Token { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } -} diff --git a/src/Core/Types/Neobook/NeobookUser.cs b/src/Core/Types/Neobook/NeobookUser.cs deleted file mode 100644 index 3aacfef7..00000000 --- a/src/Core/Types/Neobook/NeobookUser.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Neobook; - -public class NeobookUser -{ - [JsonPropertyName("username")] public string UserName { get; set; } - - [JsonPropertyName("firstname")] public string FirstName { get; set; } - - [JsonPropertyName("lastname")] public string LastName { get; set; } -} diff --git a/src/Core/Types/Ranobe/RanobeBook.cs b/src/Core/Types/Ranobe/RanobeBook.cs deleted file mode 100644 index 1a1eea5d..00000000 --- a/src/Core/Types/Ranobe/RanobeBook.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Ranobe; - -public class RanobeBook -{ - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("author")] public string Author { get; set; } - - [JsonPropertyName("verticalImages")] public RanobeImage[] Images { get; set; } - - [JsonPropertyName("verticalImage")] public RanobeImage Image { get; set; } - - [JsonPropertyName("chapters")] public List Chapters { get; set; } -} diff --git a/src/Core/Types/Ranobe/RanobeChapter.cs b/src/Core/Types/Ranobe/RanobeChapter.cs deleted file mode 100644 index a2eb7fd4..00000000 --- a/src/Core/Types/Ranobe/RanobeChapter.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Ranobe; - -public class RanobeChapter -{ - [JsonPropertyName("content")] public RanobeChapterContent Content { get; set; } -} diff --git a/src/Core/Types/Ranobe/RanobeChapterContent.cs b/src/Core/Types/Ranobe/RanobeChapterContent.cs deleted file mode 100644 index ae84c90c..00000000 --- a/src/Core/Types/Ranobe/RanobeChapterContent.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Ranobe; - -public class RanobeChapterContent -{ - [JsonPropertyName("text")] public string Text { get; set; } -} diff --git a/src/Core/Types/Ranobe/RanobeChapterShort.cs b/src/Core/Types/Ranobe/RanobeChapterShort.cs deleted file mode 100644 index eec594f9..00000000 --- a/src/Core/Types/Ranobe/RanobeChapterShort.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Ranobe; - -public class RanobeChapterShort -{ - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("url")] public string Url { get; set; } -} diff --git a/src/Core/Types/Ranobe/RanobeImage.cs b/src/Core/Types/Ranobe/RanobeImage.cs deleted file mode 100644 index 6f1e00c7..00000000 --- a/src/Core/Types/Ranobe/RanobeImage.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Ranobe; - -public class RanobeImage -{ - [JsonPropertyName("height")] public int Height { get; set; } - - [JsonPropertyName("url")] public string Url { get; set; } -} diff --git a/src/Core/Types/RanobeHub/RanobeHubApiResponse.cs b/src/Core/Types/RanobeHub/RanobeHubApiResponse.cs deleted file mode 100644 index 7f834b20..00000000 --- a/src/Core/Types/RanobeHub/RanobeHubApiResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeHub; - -public class RanobeHubApiResponse -{ - [JsonPropertyName("volumes")] public RanobeHubVolume[] Volumes { get; set; } -} diff --git a/src/Core/Types/RanobeHub/RanobeHubChapter.cs b/src/Core/Types/RanobeHub/RanobeHubChapter.cs deleted file mode 100644 index c92d9904..00000000 --- a/src/Core/Types/RanobeHub/RanobeHubChapter.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeHub; - -public class RanobeHubChapter -{ - [JsonPropertyName("name")] public string Name { get; set; } - - [JsonPropertyName("url")] public string Url { get; set; } -} diff --git a/src/Core/Types/RanobeHub/RanobeHubVolume.cs b/src/Core/Types/RanobeHub/RanobeHubVolume.cs deleted file mode 100644 index d2131c9c..00000000 --- a/src/Core/Types/RanobeHub/RanobeHubVolume.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeHub; - -public class RanobeHubVolume -{ - [JsonPropertyName("chapters")] public RanobeHubChapter[] Chapters { get; set; } -} diff --git a/src/Core/Types/RanobeNovels/RanobeNovelsChapter.cs b/src/Core/Types/RanobeNovels/RanobeNovelsChapter.cs deleted file mode 100644 index f69cb4a5..00000000 --- a/src/Core/Types/RanobeNovels/RanobeNovelsChapter.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeNovels; - -public class RanobeNovelsChapter -{ - [JsonPropertyName("post_name")] public string Name { get; set; } - - [JsonPropertyName("post_title")] public string Title { get; set; } -} diff --git a/src/Core/Types/RanobeOvh/RanobeOvhBranch.cs b/src/Core/Types/RanobeOvh/RanobeOvhBranch.cs deleted file mode 100644 index f9c1a255..00000000 --- a/src/Core/Types/RanobeOvh/RanobeOvhBranch.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeOvh; - -public class RanobeOvhBranch -{ - [JsonPropertyName("id")] public string Id { get; set; } - - [JsonPropertyName("translators")] public RanoveOvhTranslator[] Translators { get; set; } - - [JsonPropertyName("chaptersCount")] public long ChaptersCount { get; set; } -} diff --git a/src/Core/Types/RanobeOvh/RanobeOvhChapterFull.cs b/src/Core/Types/RanobeOvh/RanobeOvhChapterFull.cs deleted file mode 100644 index 6126c7cf..00000000 --- a/src/Core/Types/RanobeOvh/RanobeOvhChapterFull.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeOvh; - -public class RanobeOvhChapterFull -{ - [JsonPropertyName("pages")] public RanobeOvhPage[] Pages { get; set; } - - [JsonPropertyName("content")] public JsonNode Content { get; set; } -} - -public class RanobeOvhChapterShort -{ - [JsonPropertyName("id")] public string Id { get; set; } - - [JsonPropertyName("number")] public decimal Number { get; set; } - - [JsonPropertyName("volume")] public int? Volume { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("branchId")] public string BranchId { get; set; } - - public string FullName - { - get - { - if (!Volume.HasValue) - { - return Title; - } - - var shortName = $"Том {Volume}. Глава {(int)Number}"; - return string.IsNullOrWhiteSpace(Title) ? shortName : Title; - } - } -} - -public class RanobeOvhChapterContent -{ - [JsonPropertyName("type")] public string Type { get; set; } - - [JsonPropertyName("text")] public string Text { get; set; } - - [JsonPropertyName("marks")] public RanobeOvhChapterMark[] Marks { get; set; } = []; - - [JsonPropertyName("content")] public RanobeOvhChapterContent[] Content { get; set; } = []; - - [JsonPropertyName("attrs")] public Dictionary Attrs { get; set; } = new(); -} - -public class RanobeOvhChapterMark -{ - [JsonPropertyName("type")] public string Type { get; set; } -} diff --git a/src/Core/Types/RanobeOvh/RanobeOvhManga.cs b/src/Core/Types/RanobeOvh/RanobeOvhManga.cs deleted file mode 100644 index dc8013ef..00000000 --- a/src/Core/Types/RanobeOvh/RanobeOvhManga.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeOvh; - -public class RanobeOvhManga -{ - [JsonPropertyName("name")] public RanobeOvhMangaName Name { get; set; } - - [JsonPropertyName("poster")] public string Poster { get; set; } - - [JsonPropertyName("description")] public string Description { get; set; } -} diff --git a/src/Core/Types/RanobeOvh/RanobeOvhMangaName.cs b/src/Core/Types/RanobeOvh/RanobeOvhMangaName.cs deleted file mode 100644 index 6e3f66f6..00000000 --- a/src/Core/Types/RanobeOvh/RanobeOvhMangaName.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeOvh; - -public class RanobeOvhMangaName -{ - [JsonPropertyName("ru")] public string Ru { get; set; } -} diff --git a/src/Core/Types/RanobeOvh/RanobeOvhPage.cs b/src/Core/Types/RanobeOvh/RanobeOvhPage.cs deleted file mode 100644 index 4bf91bad..00000000 --- a/src/Core/Types/RanobeOvh/RanobeOvhPage.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeOvh; - -public class RanobeOvhPage -{ - [JsonPropertyName("id")] public string Id { get; set; } - - [JsonPropertyName("image")] public string Image { get; set; } -} diff --git a/src/Core/Types/RanobeOvh/RanoveOvhTranslator.cs b/src/Core/Types/RanobeOvh/RanoveOvhTranslator.cs deleted file mode 100644 index 4f2c853b..00000000 --- a/src/Core/Types/RanobeOvh/RanoveOvhTranslator.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.RanobeOvh; - -public class RanoveOvhTranslator -{ - [JsonPropertyName("name")] public string Name { get; set; } - - [JsonPropertyName("slug")] public string Slug { get; set; } -} diff --git a/src/Core/Types/Ranobes/RanobesCookie.cs b/src/Core/Types/Ranobes/RanobesCookie.cs deleted file mode 100644 index 1603d3e8..00000000 --- a/src/Core/Types/Ranobes/RanobesCookie.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Ranobes; - -public class RanobesCookie -{ - [JsonPropertyName("cookie")] public string Cookie { get; set; } -} diff --git a/src/Core/Types/Renovels/RenovelsApiResponse.cs b/src/Core/Types/Renovels/RenovelsApiResponse.cs deleted file mode 100644 index 5053e6ec..00000000 --- a/src/Core/Types/Renovels/RenovelsApiResponse.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Renovels; - -public class RenovelsApiResponse -{ - [JsonPropertyName("msg")] public string Message { get; set; } - - [JsonPropertyName("content")] public T Content { get; set; } -} diff --git a/src/Core/Types/Renovels/RenovelsAuthResponse.cs b/src/Core/Types/Renovels/RenovelsAuthResponse.cs deleted file mode 100644 index e5157d59..00000000 --- a/src/Core/Types/Renovels/RenovelsAuthResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Renovels; - -public class RenovelsAuthResponse -{ - [JsonPropertyName("access_token")] public string Token { get; set; } -} diff --git a/src/Core/Types/Renovels/RenovelsBranch.cs b/src/Core/Types/Renovels/RenovelsBranch.cs deleted file mode 100644 index 350b6c3f..00000000 --- a/src/Core/Types/Renovels/RenovelsBranch.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Renovels; - -public class RenovelsBranch -{ - [JsonPropertyName("id")] public int Id { get; set; } - - [JsonPropertyName("publishers")] public RenovelsPublisher[] Publishers { get; set; } -} diff --git a/src/Core/Types/Renovels/RenovelsChapter.cs b/src/Core/Types/Renovels/RenovelsChapter.cs deleted file mode 100644 index 741fedde..00000000 --- a/src/Core/Types/Renovels/RenovelsChapter.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -namespace Core.Types.Renovels; - -public class RenovelsChapter -{ - [JsonPropertyName("id")] public int Id { get; set; } - - [JsonPropertyName("chapter")] public string Chapter { get; set; } - - [JsonPropertyName("name")] public string Name { get; set; } - - [JsonPropertyName("tome")] public int Tome { get; set; } - - [JsonPropertyName("content")] public string Content { get; set; } - - [JsonPropertyName("is_paid")] public bool IsPaid { get; set; } - - [JsonPropertyName("is_bought")] public bool? IsBought { get; set; } - - [JsonPropertyName("pages")] public JsonArray Pages { get; set; } - - public string Title - { - get - { - var name = $"Том {Tome}. Глава {Chapter}"; - if (string.IsNullOrWhiteSpace(Name)) - { - return name; - } - - return name + $" - {Name}"; - } - } -} diff --git a/src/Core/Types/Renovels/RenovelsContent.cs b/src/Core/Types/Renovels/RenovelsContent.cs deleted file mode 100644 index 3521252a..00000000 --- a/src/Core/Types/Renovels/RenovelsContent.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Renovels; - -public class RenovelsContent -{ - [JsonPropertyName("cover")] public Dictionary Cover { get; set; } - - [JsonPropertyName("main_name")] public string MainName { get; set; } - - [JsonPropertyName("secondary_name")] public string SecondaryName { get; set; } - - [JsonPropertyName("another_name")] public string AnotherName { get; set; } - - [JsonPropertyName("description")] public string Description { get; set; } - - [JsonPropertyName("branches")] public RenovelsBranch[] Branches { get; set; } -} diff --git a/src/Core/Types/Renovels/RenovelsPage.cs b/src/Core/Types/Renovels/RenovelsPage.cs deleted file mode 100644 index eaa0c498..00000000 --- a/src/Core/Types/Renovels/RenovelsPage.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Renovels; - -public class RenovelsPage -{ - [JsonPropertyName("link")] public string Link { get; set; } -} diff --git a/src/Core/Types/Renovels/RenovelsPublisher.cs b/src/Core/Types/Renovels/RenovelsPublisher.cs deleted file mode 100644 index dbb794f5..00000000 --- a/src/Core/Types/Renovels/RenovelsPublisher.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Renovels; - -public class RenovelsPublisher -{ - [JsonPropertyName("name")] public string Name { get; set; } - - [JsonPropertyName("dir")] public string Dir { get; set; } -} diff --git a/src/Core/Types/Renovels/RenovelsTocResponse.cs b/src/Core/Types/Renovels/RenovelsTocResponse.cs deleted file mode 100644 index d3b194f7..00000000 --- a/src/Core/Types/Renovels/RenovelsTocResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Renovels; - -public class RenovelsTocResponse -{ - [JsonPropertyName("results")] public RenovelsChapter[] Results { get; set; } -} diff --git a/src/Core/Types/Rulate/RulateAuthResponse.cs b/src/Core/Types/Rulate/RulateAuthResponse.cs deleted file mode 100644 index 8d1c48c0..00000000 --- a/src/Core/Types/Rulate/RulateAuthResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Rulate; - -public class RulateAuthResponse -{ - [JsonPropertyName("error")] public string Error { get; set; } -} diff --git a/src/Core/Types/Rulate/RulateChapter.cs b/src/Core/Types/Rulate/RulateChapter.cs deleted file mode 100644 index ce64e866..00000000 --- a/src/Core/Types/Rulate/RulateChapter.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Rulate; - -public class RulateChapter -{ - [JsonPropertyName("can_read")] public bool CanRead { get; set; } - - [JsonPropertyName("content")] public string Content { get; set; } -} diff --git a/src/Core/Types/SocialLib/LibSocialToken.cs b/src/Core/Types/SocialLib/LibSocialToken.cs deleted file mode 100644 index 01b67789..00000000 --- a/src/Core/Types/SocialLib/LibSocialToken.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.SocialLib; - -public class LibSocialToken -{ - [JsonPropertyName("access_token")] public string AccessToken { get; set; } -} diff --git a/src/Core/Types/SocialLib/RanobeLibBookDetail.cs b/src/Core/Types/SocialLib/RanobeLibBookDetail.cs deleted file mode 100644 index 60f17bfd..00000000 --- a/src/Core/Types/SocialLib/RanobeLibBookDetail.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.SocialLib; - -public class RanobeLibBookDetails -{ - [JsonPropertyName("data")] public RLBDData Data { get; set; } -} - -public class RLBDData -{ - public RLBDData(int? id) - { - Id = id; - } - - [JsonPropertyName("id")] public int? Id { get; set; } - - [JsonPropertyName("name")] public string Name { get; set; } - - [JsonPropertyName("rus_name")] public string RusName { get; set; } - - [JsonPropertyName("slug_url")] public string SlugUrl { get; set; } - - [JsonPropertyName("cover")] public RLBDCover Cover { get; set; } - - [JsonPropertyName("authors")] public List Authors { get; set; } - - [JsonPropertyName("summary")] public string Summary { get; set; } -} - -public class RLBDCover -{ - [JsonPropertyName("default")] public string Default { get; set; } -} - -public class RLBDAuthor -{ - [JsonPropertyName("name")] public string Name { get; set; } - - [JsonPropertyName("slug_url")] public string SlugUrl { get; set; } -} diff --git a/src/Core/Types/SocialLib/SocialLibBookChapters.cs b/src/Core/Types/SocialLib/SocialLibBookChapters.cs deleted file mode 100644 index c7b57e57..00000000 --- a/src/Core/Types/SocialLib/SocialLibBookChapters.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -namespace Core.Types.SocialLib; - -public class SocialLibBookChapters -{ - [JsonPropertyName("data")] public List Chapters { get; set; } -} - -public class SocialLibBookChapterResponse -{ - [JsonPropertyName("data")] public SocialLibBookChapter Data { get; set; } -} - -public class SocialLibChapterBranch -{ - [JsonPropertyName("branch_id")] public long? BranchId { get; set; } -} - -public class SocialLibChapterAttachment -{ - [JsonPropertyName("url")] public string Url { get; set; } - - [JsonPropertyName("name")] public string Name { get; set; } -} - -public class SocialLibBookChapter -{ - [JsonPropertyName("volume")] public string Volume { get; set; } - - [JsonPropertyName("number")] public string Number { get; set; } - - [JsonPropertyName("branches")] public List Branches { get; set; } - - [JsonPropertyName("attachments")] public SocialLibChapterAttachment[] Attachments { get; set; } = []; - - [JsonPropertyName("pages")] public SocialLibPage[] Pages { get; set; } = []; - - [JsonPropertyName("content")] public JsonNode Content { get; set; } - - private string RawName { get; set; } - - [JsonPropertyName("name")] - public string Name - { - get - { - var name = $"Том {Volume} Глава {Number}"; - if (!string.IsNullOrWhiteSpace(RawName)) - { - name += $" - {RawName}"; - } - - return name; - } - set { RawName = value; } - } -} - -public class SocialLibChapterContent -{ - [JsonPropertyName("type")] public string Type { get; set; } - - [JsonPropertyName("text")] public string Text { get; set; } - - [JsonPropertyName("marks")] public SocialLibChapterMark[] Marks { get; set; } = []; - - [JsonPropertyName("content")] public SocialLibChapterContent[] Content { get; set; } = []; - - [JsonPropertyName("attrs")] public Dictionary Attrs { get; set; } = new(); -} - -public class SocialLibChapterMark -{ - [JsonPropertyName("type")] public string Type { get; set; } -} - -public class SocialLibPage -{ - [JsonPropertyName("url")] public string Url { get; set; } -} diff --git a/src/Core/Types/SocialLib/SocialLibChapter.cs b/src/Core/Types/SocialLib/SocialLibChapter.cs deleted file mode 100644 index 7473fe0a..00000000 --- a/src/Core/Types/SocialLib/SocialLibChapter.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.SocialLib; - -public class SocialLibChapter -{ - [JsonPropertyName("chapter_number")] public string ChapterNumber { get; set; } - - [JsonPropertyName("chapter_volume")] public int ChapterVolume { get; set; } - - [JsonPropertyName("chapter_name")] public string ChapterName { get; set; } - - [JsonPropertyName("chapter_slug")] public string ChapterSlug { get; set; } - - [JsonPropertyName("branch_id")] public int? BranchId { get; set; } - - public string Name => $"Том {ChapterVolume} Глава {ChapterNumber} {ChapterName}".Trim(); -} diff --git a/src/Core/Types/SocialLib/SocialLibChapterOld.cs b/src/Core/Types/SocialLib/SocialLibChapterOld.cs deleted file mode 100644 index 039797af..00000000 --- a/src/Core/Types/SocialLib/SocialLibChapterOld.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.SocialLib; - -public class SocialLibChapterOld -{ - [JsonPropertyName("number")] public string ChapterNumber { get; set; } - - [JsonPropertyName("volume")] public string ChapterVolume { get; set; } - - [JsonPropertyName("name")] public string ChapterName { get; set; } - - [JsonPropertyName("slug")] public string ChapterSlug { get; set; } - - public string GetName() - { - return $"Том {ChapterVolume} Глава {ChapterNumber} {ChapterName}".Trim(); - } -} diff --git a/src/Core/Types/SocialLib/SocialLibChapters.cs b/src/Core/Types/SocialLib/SocialLibChapters.cs deleted file mode 100644 index ade446c4..00000000 --- a/src/Core/Types/SocialLib/SocialLibChapters.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.SocialLib; - -public class SocialLibChapters -{ - [JsonPropertyName("list")] public List List { get; set; } -} diff --git a/src/Core/Types/SocialLib/User.cs b/src/Core/Types/SocialLib/User.cs deleted file mode 100644 index 5cc19a89..00000000 --- a/src/Core/Types/SocialLib/User.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.SocialLib; - -public class User -{ - [JsonPropertyName("id")] public long Id { get; set; } -} diff --git a/src/Core/Types/SocialLib/WindowData.cs b/src/Core/Types/SocialLib/WindowData.cs deleted file mode 100644 index a6cc2dff..00000000 --- a/src/Core/Types/SocialLib/WindowData.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.SocialLib; - -public class WindowData -{ - [JsonPropertyName("chapters")] public SocialLibChapters Chapters { get; set; } - - [JsonPropertyName("user")] public User User { get; set; } -} diff --git a/src/Core/Types/StrokiMts/StrokiMtsApiMultiResponse.cs b/src/Core/Types/StrokiMts/StrokiMtsApiMultiResponse.cs deleted file mode 100644 index f3132490..00000000 --- a/src/Core/Types/StrokiMts/StrokiMtsApiMultiResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsApiMultiResponse -{ - [JsonPropertyName("items")] public List Items { get; set; } -} diff --git a/src/Core/Types/StrokiMts/StrokiMtsApiResponse.cs b/src/Core/Types/StrokiMts/StrokiMtsApiResponse.cs deleted file mode 100644 index 219c82fd..00000000 --- a/src/Core/Types/StrokiMts/StrokiMtsApiResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsApiResponse -{ - [JsonPropertyName("data")] public T Data { get; set; } -} diff --git a/src/Core/Types/StrokiMts/StrokiMtsAuthor.cs b/src/Core/Types/StrokiMts/StrokiMtsAuthor.cs deleted file mode 100644 index 3e91280d..00000000 --- a/src/Core/Types/StrokiMts/StrokiMtsAuthor.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsAuthor -{ - [JsonPropertyName("name")] public string Name { get; set; } - - [JsonPropertyName("friendlyUrl")] public string FriendlyUrl { get; set; } -} diff --git a/src/Core/Types/StrokiMts/StrokiMtsBookItem.cs b/src/Core/Types/StrokiMts/StrokiMtsBookItem.cs deleted file mode 100644 index f6eff794..00000000 --- a/src/Core/Types/StrokiMts/StrokiMtsBookItem.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsBookItem -{ - [JsonPropertyName("id")] public long Id { get; set; } - - [JsonPropertyName("annotation")] public string Annotation { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } - - [JsonPropertyName("imageUrl")] public Dictionary ImageUrl { get; set; } - - [JsonPropertyName("authors")] public List Authors { get; set; } - - [JsonPropertyName("contentInfo")] public StrokiMtsContentInfo ContentInfo { get; set; } -} diff --git a/src/Core/Types/StrokiMts/StrokiMtsContentInfo.cs b/src/Core/Types/StrokiMts/StrokiMtsContentInfo.cs deleted file mode 100644 index b838d12f..00000000 --- a/src/Core/Types/StrokiMts/StrokiMtsContentInfo.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsContentInfo -{ - [JsonPropertyName("fileType")] public string FileType { get; set; } -} diff --git a/src/Core/Types/StrokiMts/StrokiMtsFile.cs b/src/Core/Types/StrokiMts/StrokiMtsFile.cs deleted file mode 100644 index f2da4ce2..00000000 --- a/src/Core/Types/StrokiMts/StrokiMtsFile.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsFile -{ - [JsonPropertyName("fileId")] public long FileId { get; set; } -} diff --git a/src/Core/Types/StrokiMts/StrokiMtsFileUrl.cs b/src/Core/Types/StrokiMts/StrokiMtsFileUrl.cs deleted file mode 100644 index b111fca1..00000000 --- a/src/Core/Types/StrokiMts/StrokiMtsFileUrl.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsFileUrl -{ - [JsonPropertyName("url")] public string Url { get; set; } -} diff --git a/src/Core/Types/StrokiMts/StrokiMtsFiles.cs b/src/Core/Types/StrokiMts/StrokiMtsFiles.cs deleted file mode 100644 index 01fa6236..00000000 --- a/src/Core/Types/StrokiMts/StrokiMtsFiles.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsFiles -{ - [JsonPropertyName("preview")] public StrokiMtsFile Preview { get; set; } - - [JsonPropertyName("full")] public StrokiMtsFile[] Full { get; set; } -} diff --git a/src/Core/Types/StrokiMts/StrokiMtsMultiItem.cs b/src/Core/Types/StrokiMts/StrokiMtsMultiItem.cs deleted file mode 100644 index 5d2502a8..00000000 --- a/src/Core/Types/StrokiMts/StrokiMtsMultiItem.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.StrokiMts; - -public class StrokiMtsMultiItem -{ - [JsonPropertyName("textBook")] public StrokiMtsBookItem TextBook { get; set; } - - [JsonPropertyName("audioBook")] public StrokiMtsBookItem AudioBook { get; set; } - - [JsonPropertyName("comics")] public StrokiMtsBookItem ComicBook { get; set; } - - [JsonPropertyName("pressa")] public StrokiMtsBookItem PressBook { get; set; } -} diff --git a/src/Core/Types/Wattpad/WattpadInfo.cs b/src/Core/Types/Wattpad/WattpadInfo.cs deleted file mode 100644 index 1ae50bf8..00000000 --- a/src/Core/Types/Wattpad/WattpadInfo.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Wattpad; - -public class WattpadInfo -{ - [JsonPropertyName("user")] public WattpadUser User { get; set; } - - [JsonPropertyName("cover")] public string Cover { get; set; } - - [JsonPropertyName("parts")] public WattpadPart[] Parts { get; set; } - - [JsonPropertyName("description")] public string Description { get; set; } - - [JsonPropertyName("title")] public string Title { get; set; } -} diff --git a/src/Core/Types/Wattpad/WattpadMeta.cs b/src/Core/Types/Wattpad/WattpadMeta.cs deleted file mode 100644 index 13f106f6..00000000 --- a/src/Core/Types/Wattpad/WattpadMeta.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Core.Types.Wattpad; - -public class WattpadMeta -{ - public string StoryId { get; set; } -} diff --git a/src/Core/Types/Wattpad/WattpadPart.cs b/src/Core/Types/Wattpad/WattpadPart.cs deleted file mode 100644 index 3d62d68b..00000000 --- a/src/Core/Types/Wattpad/WattpadPart.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Text.Json.Serialization; -using Core.Extensions; - -namespace Core.Types.Wattpad; - -public class WattpadPart -{ - [JsonPropertyName("id")] public long Id { get; set; } - - [JsonPropertyName("title")] public string Title { private get; set; } - - public string FullName => Title.Replace("\r", " ").CollapseWhitespace().Trim(); - - public Uri Url => new($"https://www.wattpad.com/apiv2/storytext?id={Id}"); -} diff --git a/src/Core/Types/Wattpad/WattpadUser.cs b/src/Core/Types/Wattpad/WattpadUser.cs deleted file mode 100644 index f5990de3..00000000 --- a/src/Core/Types/Wattpad/WattpadUser.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.Wattpad; - -public class WattpadUser -{ - [JsonPropertyName("name")] public string Name { get; set; } -} diff --git a/src/Core/Types/WuxiaWorld/WuxiaWorldToc.cs b/src/Core/Types/WuxiaWorld/WuxiaWorldToc.cs deleted file mode 100644 index c814037c..00000000 --- a/src/Core/Types/WuxiaWorld/WuxiaWorldToc.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Types.WuxiaWorld; - -public class WuxiaWorldToc -{ - [JsonPropertyName("post_name")] public string PostName { get; set; } -} diff --git a/src/Core/Types/Book/AdditionalFileCollection.cs b/src/Elib2Ebook.Domain/Book/AdditionalFileCollection.cs similarity index 92% rename from src/Core/Types/Book/AdditionalFileCollection.cs rename to src/Elib2Ebook.Domain/Book/AdditionalFileCollection.cs index 931b73c4..fb1539ca 100644 --- a/src/Core/Types/Book/AdditionalFileCollection.cs +++ b/src/Elib2Ebook.Domain/Book/AdditionalFileCollection.cs @@ -1,7 +1,6 @@ -using Core.Misc; -using Core.Types.Common; +using Elib2Ebook.Domain.Common; -namespace Core.Types.Book; +namespace Elib2Ebook.Domain.Book; public class AdditionalFileCollection : IDisposable { diff --git a/src/Core/Misc/AdditionalTypeEnum.cs b/src/Elib2Ebook.Domain/Book/AdditionalTypeEnum.cs similarity index 72% rename from src/Core/Misc/AdditionalTypeEnum.cs rename to src/Elib2Ebook.Domain/Book/AdditionalTypeEnum.cs index ca1a322d..5962fa36 100644 --- a/src/Core/Misc/AdditionalTypeEnum.cs +++ b/src/Elib2Ebook.Domain/Book/AdditionalTypeEnum.cs @@ -1,4 +1,4 @@ -namespace Core.Misc; +namespace Elib2Ebook.Domain.Book; [Flags] public enum AdditionalTypeEnum diff --git a/src/Core/Types/Book/Author.cs b/src/Elib2Ebook.Domain/Book/Author.cs similarity index 90% rename from src/Core/Types/Book/Author.cs rename to src/Elib2Ebook.Domain/Book/Author.cs index 685723c7..9e6389b5 100644 --- a/src/Core/Types/Book/Author.cs +++ b/src/Elib2Ebook.Domain/Book/Author.cs @@ -1,4 +1,4 @@ -namespace Core.Types.Book; +namespace Elib2Ebook.Domain.Book; public class Author { diff --git a/src/Core/Types/Book/Book.cs b/src/Elib2Ebook.Domain/Book/Book.cs similarity index 95% rename from src/Core/Types/Book/Book.cs rename to src/Elib2Ebook.Domain/Book/Book.cs index ed913718..d963f619 100644 --- a/src/Core/Types/Book/Book.cs +++ b/src/Elib2Ebook.Domain/Book/Book.cs @@ -1,6 +1,6 @@ -using Core.Types.Common; +using Elib2Ebook.Domain.Common; -namespace Core.Types.Book; +namespace Elib2Ebook.Domain.Book; public class Book : IDisposable { diff --git a/src/Core/Types/Book/Chapter.cs b/src/Elib2Ebook.Domain/Book/Chapter.cs similarity index 89% rename from src/Core/Types/Book/Chapter.cs rename to src/Elib2Ebook.Domain/Book/Chapter.cs index 0cc07bb4..d22f1a35 100644 --- a/src/Core/Types/Book/Chapter.cs +++ b/src/Elib2Ebook.Domain/Book/Chapter.cs @@ -1,6 +1,6 @@ -using Core.Types.Common; +using Elib2Ebook.Domain.Common; -namespace Core.Types.Book; +namespace Elib2Ebook.Domain.Book; public class Chapter { diff --git a/src/Core/Types/Book/Seria.cs b/src/Elib2Ebook.Domain/Book/Seria.cs similarity index 91% rename from src/Core/Types/Book/Seria.cs rename to src/Elib2Ebook.Domain/Book/Seria.cs index 969f2045..1f199123 100644 --- a/src/Core/Types/Book/Seria.cs +++ b/src/Elib2Ebook.Domain/Book/Seria.cs @@ -1,4 +1,4 @@ -namespace Core.Types.Book; +namespace Elib2Ebook.Domain.Book; public class Seria { diff --git a/src/Core/Types/Common/IdChapter.cs b/src/Elib2Ebook.Domain/Common/IdChapter.cs similarity index 58% rename from src/Core/Types/Common/IdChapter.cs rename to src/Elib2Ebook.Domain/Common/IdChapter.cs index 82a703ac..9b812eee 100644 --- a/src/Core/Types/Common/IdChapter.cs +++ b/src/Elib2Ebook.Domain/Common/IdChapter.cs @@ -1,3 +1,3 @@ -namespace Core.Types.Common; +namespace Elib2Ebook.Domain.Common; public record IdChapter(string Id, string Title); diff --git a/src/Core/Types/Common/TempFile.cs b/src/Elib2Ebook.Domain/Common/TempFile.cs similarity index 98% rename from src/Core/Types/Common/TempFile.cs rename to src/Elib2Ebook.Domain/Common/TempFile.cs index 07605673..56c4f1b2 100644 --- a/src/Core/Types/Common/TempFile.cs +++ b/src/Elib2Ebook.Domain/Common/TempFile.cs @@ -1,4 +1,4 @@ -namespace Core.Types.Common; +namespace Elib2Ebook.Domain.Common; public class TempFile : IDisposable { diff --git a/src/Core/Types/Common/UrlChapter.cs b/src/Elib2Ebook.Domain/Common/UrlChapter.cs similarity index 58% rename from src/Core/Types/Common/UrlChapter.cs rename to src/Elib2Ebook.Domain/Common/UrlChapter.cs index 0a406d4e..8c988e5c 100644 --- a/src/Core/Types/Common/UrlChapter.cs +++ b/src/Elib2Ebook.Domain/Common/UrlChapter.cs @@ -1,3 +1,3 @@ -namespace Core.Types.Common; +namespace Elib2Ebook.Domain.Common; public record UrlChapter(Uri Url, string Title); diff --git a/src/Elib2Ebook.Domain/Elib2Ebook.Domain.csproj b/src/Elib2Ebook.Domain/Elib2Ebook.Domain.csproj new file mode 100644 index 00000000..c6321610 --- /dev/null +++ b/src/Elib2Ebook.Domain/Elib2Ebook.Domain.csproj @@ -0,0 +1,3 @@ + + + diff --git a/src/Core/Logic/BookNameBuilder.cs b/src/Elib2Ebook.DomainServices/BookNameBuilder.cs similarity index 86% rename from src/Core/Logic/BookNameBuilder.cs rename to src/Elib2Ebook.DomainServices/BookNameBuilder.cs index 72ac8de6..57c868a8 100644 --- a/src/Core/Logic/BookNameBuilder.cs +++ b/src/Elib2Ebook.DomainServices/BookNameBuilder.cs @@ -1,8 +1,8 @@ -using Core.Extensions; -using Core.Types.Book; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.DomainServices.Extensions; using StringTokenFormatter; -namespace Core.Logic; +namespace Elib2Ebook.DomainServices; public class BookNameBuilder { diff --git a/src/Core/Logic/Builders/AdditionaFileBuilder.cs b/src/Elib2Ebook.DomainServices/Builders/AdditionaFileBuilder.cs similarity index 95% rename from src/Core/Logic/Builders/AdditionaFileBuilder.cs rename to src/Elib2Ebook.DomainServices/Builders/AdditionaFileBuilder.cs index 1d40fb25..3d3e6621 100644 --- a/src/Core/Logic/Builders/AdditionaFileBuilder.cs +++ b/src/Elib2Ebook.DomainServices/Builders/AdditionaFileBuilder.cs @@ -1,10 +1,9 @@ -using Core.Configs; -using Core.Extensions; -using Core.Misc; -using Core.Types.Book; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; using Microsoft.Extensions.Logging; -namespace Core.Logic.Builders; +namespace Elib2Ebook.DomainServices.Builders; public class AdditionaFileBuilder(Options options, ILogger logger) { diff --git a/src/Core/Logic/Builders/BuilderBase.cs b/src/Elib2Ebook.DomainServices/Builders/BuilderBase.cs similarity index 93% rename from src/Core/Logic/Builders/BuilderBase.cs rename to src/Elib2Ebook.DomainServices/Builders/BuilderBase.cs index 97fa4355..c2d28087 100644 --- a/src/Core/Logic/Builders/BuilderBase.cs +++ b/src/Elib2Ebook.DomainServices/Builders/BuilderBase.cs @@ -1,10 +1,10 @@ -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; using Microsoft.Extensions.Logging; -namespace Core.Logic.Builders; +namespace Elib2Ebook.DomainServices.Builders; public abstract class BuilderBase(Options options, ILogger logger) { diff --git a/src/Core/Logic/Builders/CbzBuilder.cs b/src/Elib2Ebook.DomainServices/Builders/CbzBuilder.cs similarity index 89% rename from src/Core/Logic/Builders/CbzBuilder.cs rename to src/Elib2Ebook.DomainServices/Builders/CbzBuilder.cs index e56b3626..b236222b 100644 --- a/src/Core/Logic/Builders/CbzBuilder.cs +++ b/src/Elib2Ebook.DomainServices/Builders/CbzBuilder.cs @@ -1,9 +1,9 @@ using System.IO.Compression; -using Core.Configs; -using Core.Types.Book; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.DomainServices.Configs; using Microsoft.Extensions.Logging; -namespace Core.Logic.Builders; +namespace Elib2Ebook.DomainServices.Builders; public class CbzBuilder(Options options, ILogger logger) : BuilderBase(options, logger) { diff --git a/src/Core/Logic/Builders/EpubBuilder.cs b/src/Elib2Ebook.DomainServices/Builders/EpubBuilder.cs similarity index 95% rename from src/Core/Logic/Builders/EpubBuilder.cs rename to src/Elib2Ebook.DomainServices/Builders/EpubBuilder.cs index d45b8bbe..876c9831 100644 --- a/src/Core/Logic/Builders/EpubBuilder.cs +++ b/src/Elib2Ebook.DomainServices/Builders/EpubBuilder.cs @@ -1,12 +1,12 @@ -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; using EpubSharp; using EpubSharp.Format; using Microsoft.Extensions.Logging; -namespace Core.Logic.Builders; +namespace Elib2Ebook.DomainServices.Builders; public class EpubBuilder(Options options, ILogger logger) : BuilderBase(options, logger) { diff --git a/src/Core/Logic/Builders/Fb2Builder.cs b/src/Elib2Ebook.DomainServices/Builders/Fb2Builder.cs similarity index 98% rename from src/Core/Logic/Builders/Fb2Builder.cs rename to src/Elib2Ebook.DomainServices/Builders/Fb2Builder.cs index baa1ded2..6ec0e72c 100644 --- a/src/Core/Logic/Builders/Fb2Builder.cs +++ b/src/Elib2Ebook.DomainServices/Builders/Fb2Builder.cs @@ -1,14 +1,14 @@ using System.Text; using System.Xml; using System.Xml.Linq; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; using HtmlAgilityPack; using Microsoft.Extensions.Logging; -namespace Core.Logic.Builders; +namespace Elib2Ebook.DomainServices.Builders; public class Fb2Builder : BuilderBase { diff --git a/src/Core/Logic/Builders/JsonBuilder.cs b/src/Elib2Ebook.DomainServices/Builders/JsonBuilder.cs similarity index 82% rename from src/Core/Logic/Builders/JsonBuilder.cs rename to src/Elib2Ebook.DomainServices/Builders/JsonBuilder.cs index 3093d966..bd45e648 100644 --- a/src/Core/Logic/Builders/JsonBuilder.cs +++ b/src/Elib2Ebook.DomainServices/Builders/JsonBuilder.cs @@ -1,9 +1,9 @@ using System.Text.Json; -using Core.Configs; -using Core.Types.Book; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.DomainServices.Configs; using Microsoft.Extensions.Logging; -namespace Core.Logic.Builders; +namespace Elib2Ebook.DomainServices.Builders; public class JsonBuilder(Options options, ILogger logger) : BuilderBase(options, logger) { diff --git a/src/Core/Logic/Builders/JsonLiteBuilder.cs b/src/Elib2Ebook.DomainServices/Builders/JsonLiteBuilder.cs similarity index 94% rename from src/Core/Logic/Builders/JsonLiteBuilder.cs rename to src/Elib2Ebook.DomainServices/Builders/JsonLiteBuilder.cs index ecd030b0..00b9e401 100644 --- a/src/Core/Logic/Builders/JsonLiteBuilder.cs +++ b/src/Elib2Ebook.DomainServices/Builders/JsonLiteBuilder.cs @@ -1,11 +1,11 @@ using System.Text.Json; using System.Text.Json.Serialization; -using Core.Configs; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; using Microsoft.Extensions.Logging; -namespace Core.Logic.Builders; +namespace Elib2Ebook.DomainServices.Builders; public class ShortImageConverter : JsonConverter { diff --git a/src/Core/Logic/Builders/TxtBuilder.cs b/src/Elib2Ebook.DomainServices/Builders/TxtBuilder.cs similarity index 88% rename from src/Core/Logic/Builders/TxtBuilder.cs rename to src/Elib2Ebook.DomainServices/Builders/TxtBuilder.cs index 095c3146..d9db9d5e 100644 --- a/src/Core/Logic/Builders/TxtBuilder.cs +++ b/src/Elib2Ebook.DomainServices/Builders/TxtBuilder.cs @@ -1,9 +1,9 @@ -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; using Microsoft.Extensions.Logging; -namespace Core.Logic.Builders; +namespace Elib2Ebook.DomainServices.Builders; public class TxtBuilder(Options options, ILogger logger) : BuilderBase(options, logger) { diff --git a/src/Core/Configs/BookGetterConfig.cs b/src/Elib2Ebook.DomainServices/Configs/BookGetterConfig.cs similarity index 97% rename from src/Core/Configs/BookGetterConfig.cs rename to src/Elib2Ebook.DomainServices/Configs/BookGetterConfig.cs index 3e221e66..a52daae1 100644 --- a/src/Core/Configs/BookGetterConfig.cs +++ b/src/Elib2Ebook.DomainServices/Configs/BookGetterConfig.cs @@ -1,10 +1,10 @@ using System.Net; -using Core.Extensions; -using Core.Misc.TempFolder; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Misc.TempFolder; using FlareSolverrSharp; using Microsoft.Extensions.Logging; -namespace Core.Configs; +namespace Elib2Ebook.DomainServices.Configs; public class BookGetterConfig : IDisposable { diff --git a/src/Core/Configs/Options.cs b/src/Elib2Ebook.DomainServices/Configs/Options.cs similarity index 81% rename from src/Core/Configs/Options.cs rename to src/Elib2Ebook.DomainServices/Configs/Options.cs index ab66c64a..6b8c74e3 100644 --- a/src/Core/Configs/Options.cs +++ b/src/Elib2Ebook.DomainServices/Configs/Options.cs @@ -1,7 +1,7 @@ using CommandLine; -using Core.Misc; +using Elib2Ebook.Domain.Book; -namespace Core.Configs; +namespace Elib2Ebook.DomainServices.Configs; public class Options { @@ -18,13 +18,17 @@ public Options(IEnumerable url) [Option('f', "format", Required = true, HelpText = "Формат для сохранения книги. Допустимые значения: epub, fb2, cbz, json", Separator = ',')] public IEnumerable Format { get; set; } - [Option('l', "login", Required = false, HelpText = "Логин от системы")] public string Login { get; set; } + [Option('l', "login", Required = false, HelpText = "Логин от системы")] + public string Login { get; set; } - [Option('p', "password", Required = false, HelpText = "Пароль от системы")] public string Password { get; set; } + [Option('p', "password", Required = false, HelpText = "Пароль от системы")] + public string Password { get; set; } - [Option("proxy", Required = false, HelpText = "Прокси в формате :")] public string Proxy { get; set; } + [Option("proxy", Required = false, HelpText = "Прокси в формате :")] + public string Proxy { get; set; } - [Option("flare", Required = false, HelpText = "Адрес flaresolverr")] public string Flare { get; set; } + [Option("flare", Required = false, HelpText = "Адрес flaresolverr")] + public string Flare { get; set; } [Option('s', "save", Required = false, HelpText = "Директория для сохранения книги")] public string SavePath { get; set; } @@ -38,15 +42,20 @@ public Options(IEnumerable url) [Option('d', "delay", Required = false, HelpText = "Задержка между запросами в секундах", Default = 0)] public int Delay { get; set; } - [Option("no-image", Required = false, HelpText = "Не загружать картинки")] public bool NoImage { get; set; } + [Option("no-image", Required = false, HelpText = "Не загружать картинки")] + public bool NoImage { get; set; } - [Option("start", Required = false, HelpText = "Стартовый номер главы")] public int? Start { get; set; } + [Option("start", Required = false, HelpText = "Стартовый номер главы")] + public int? Start { get; set; } - [Option("end", Required = false, HelpText = "Конечный номер главы")] public int? End { get; set; } + [Option("end", Required = false, HelpText = "Конечный номер главы")] + public int? End { get; set; } - [Option("start-name", Required = false, HelpText = "Стартовое название главы")] public string StartName { get; set; } + [Option("start-name", Required = false, HelpText = "Стартовое название главы")] + public string StartName { get; set; } - [Option("end-name", Required = false, HelpText = "Конечное название главы")] public string EndName { get; set; } + [Option("end-name", Required = false, HelpText = "Конечное название главы")] + public string EndName { get; set; } [Option("temp", Required = false, HelpText = "Папка для временного хранения картинок")] public string TempPath { get; set; } diff --git a/src/Core/Core.csproj b/src/Elib2Ebook.DomainServices/Elib2Ebook.DomainServices.csproj similarity index 68% rename from src/Core/Core.csproj rename to src/Elib2Ebook.DomainServices/Elib2Ebook.DomainServices.csproj index 39786beb..46496764 100644 --- a/src/Core/Core.csproj +++ b/src/Elib2Ebook.DomainServices/Elib2Ebook.DomainServices.csproj @@ -1,28 +1,33 @@ - + + + true + + + + + + - - + - - - - Always - - - External\EpubSharp.dll + + + Always + + diff --git a/src/Core/Extensions/FileProviderExtensions.cs b/src/Elib2Ebook.DomainServices/Extensions/FileProviderExtensions.cs similarity index 95% rename from src/Core/Extensions/FileProviderExtensions.cs rename to src/Elib2Ebook.DomainServices/Extensions/FileProviderExtensions.cs index 6d410805..e8a85755 100644 --- a/src/Core/Extensions/FileProviderExtensions.cs +++ b/src/Elib2Ebook.DomainServices/Extensions/FileProviderExtensions.cs @@ -1,7 +1,7 @@ using System.IO.Enumeration; using Microsoft.Extensions.FileProviders; -namespace Core.Extensions; +namespace Elib2Ebook.DomainServices.Extensions; public static class FileProviderExtensions { diff --git a/src/Core/Extensions/HtmlDocumentExtensions.cs b/src/Elib2Ebook.DomainServices/Extensions/HtmlDocumentExtensions.cs similarity index 97% rename from src/Core/Extensions/HtmlDocumentExtensions.cs rename to src/Elib2Ebook.DomainServices/Extensions/HtmlDocumentExtensions.cs index 33f3c818..3433aa29 100644 --- a/src/Core/Extensions/HtmlDocumentExtensions.cs +++ b/src/Elib2Ebook.DomainServices/Extensions/HtmlDocumentExtensions.cs @@ -2,7 +2,7 @@ using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; -namespace Core.Extensions; +namespace Elib2Ebook.DomainServices.Extensions; public static class HtmlDocumentExtensions { diff --git a/src/Core/Extensions/HttpClientExtensions.cs b/src/Elib2Ebook.DomainServices/Extensions/HttpClientExtensions.cs similarity index 99% rename from src/Core/Extensions/HttpClientExtensions.cs rename to src/Elib2Ebook.DomainServices/Extensions/HttpClientExtensions.cs index 9b5bb159..a41e35e4 100644 --- a/src/Core/Extensions/HttpClientExtensions.cs +++ b/src/Elib2Ebook.DomainServices/Extensions/HttpClientExtensions.cs @@ -3,7 +3,7 @@ using System.Text.Json; using HtmlAgilityPack; -namespace Core.Extensions; +namespace Elib2Ebook.DomainServices.Extensions; public static class HttpClientExtensions { diff --git a/src/Core/Extensions/ImageFormatExtensions.cs b/src/Elib2Ebook.DomainServices/Extensions/ImageFormatExtensions.cs similarity index 91% rename from src/Core/Extensions/ImageFormatExtensions.cs rename to src/Elib2Ebook.DomainServices/Extensions/ImageFormatExtensions.cs index 0e362ee7..8bdbbdff 100644 --- a/src/Core/Extensions/ImageFormatExtensions.cs +++ b/src/Elib2Ebook.DomainServices/Extensions/ImageFormatExtensions.cs @@ -1,7 +1,7 @@ using EpubSharp; using EpubSharp.Format; -namespace Core.Extensions; +namespace Elib2Ebook.DomainServices.Extensions; public static class ImageFormatExtensions { diff --git a/src/Core/Extensions/StreamExtension.cs b/src/Elib2Ebook.DomainServices/Extensions/StreamExtension.cs similarity index 85% rename from src/Core/Extensions/StreamExtension.cs rename to src/Elib2Ebook.DomainServices/Extensions/StreamExtension.cs index bd1b2582..0a8fa254 100644 --- a/src/Core/Extensions/StreamExtension.cs +++ b/src/Elib2Ebook.DomainServices/Extensions/StreamExtension.cs @@ -1,7 +1,7 @@ using System.Text; using HtmlAgilityPack; -namespace Core.Extensions; +namespace Elib2Ebook.DomainServices.Extensions; public static class StreamExtension { diff --git a/src/Core/Extensions/StringBuilderExtensions.cs b/src/Elib2Ebook.DomainServices/Extensions/StringBuilderExtensions.cs similarity index 88% rename from src/Core/Extensions/StringBuilderExtensions.cs rename to src/Elib2Ebook.DomainServices/Extensions/StringBuilderExtensions.cs index ebc9c529..bb2589f9 100644 --- a/src/Core/Extensions/StringBuilderExtensions.cs +++ b/src/Elib2Ebook.DomainServices/Extensions/StringBuilderExtensions.cs @@ -1,7 +1,7 @@ using System.Text; using HtmlAgilityPack; -namespace Core.Extensions; +namespace Elib2Ebook.DomainServices.Extensions; public static class StringBuilderExtensions { diff --git a/src/Core/Extensions/StringExtensions.cs b/src/Elib2Ebook.DomainServices/Extensions/StringExtensions.cs similarity index 98% rename from src/Core/Extensions/StringExtensions.cs rename to src/Elib2Ebook.DomainServices/Extensions/StringExtensions.cs index 2f470511..ddf3929b 100644 --- a/src/Core/Extensions/StringExtensions.cs +++ b/src/Elib2Ebook.DomainServices/Extensions/StringExtensions.cs @@ -6,7 +6,7 @@ using AngleSharp.Html.Parser; using HtmlAgilityPack; -namespace Core.Extensions; +namespace Elib2Ebook.DomainServices.Extensions; public static class StringExtensions { diff --git a/src/Core/Extensions/UriExtension.cs b/src/Elib2Ebook.DomainServices/Extensions/UriExtension.cs similarity index 97% rename from src/Core/Extensions/UriExtension.cs rename to src/Elib2Ebook.DomainServices/Extensions/UriExtension.cs index 2ea2e8d9..fbfc4056 100644 --- a/src/Core/Extensions/UriExtension.cs +++ b/src/Elib2Ebook.DomainServices/Extensions/UriExtension.cs @@ -2,7 +2,7 @@ using System.Text.RegularExpressions; using System.Web; -namespace Core.Extensions; +namespace Elib2Ebook.DomainServices.Extensions; public static class UriExtension { diff --git a/src/Core/External/EpubSharp.deps.json b/src/Elib2Ebook.DomainServices/External/EpubSharp.deps.json similarity index 100% rename from src/Core/External/EpubSharp.deps.json rename to src/Elib2Ebook.DomainServices/External/EpubSharp.deps.json diff --git a/src/Core/External/EpubSharp.dll b/src/Elib2Ebook.DomainServices/External/EpubSharp.dll similarity index 100% rename from src/Core/External/EpubSharp.dll rename to src/Elib2Ebook.DomainServices/External/EpubSharp.dll diff --git a/src/Core/External/EpubSharp.pdb b/src/Elib2Ebook.DomainServices/External/EpubSharp.pdb similarity index 100% rename from src/Core/External/EpubSharp.pdb rename to src/Elib2Ebook.DomainServices/External/EpubSharp.pdb diff --git a/src/Core/Logic/FileProvider.cs b/src/Elib2Ebook.DomainServices/FileProvider.cs similarity index 94% rename from src/Core/Logic/FileProvider.cs rename to src/Elib2Ebook.DomainServices/FileProvider.cs index da6f06bc..bf019406 100644 --- a/src/Core/Logic/FileProvider.cs +++ b/src/Elib2Ebook.DomainServices/FileProvider.cs @@ -1,7 +1,7 @@ using System.Reflection; using Microsoft.Extensions.FileProviders; -namespace Core.Logic; +namespace Elib2Ebook.DomainServices; public class FileProvider { diff --git a/src/Core/Logic/Getters/GetterBase.cs b/src/Elib2Ebook.DomainServices/Getters/GetterBase.cs similarity index 98% rename from src/Core/Logic/Getters/GetterBase.cs rename to src/Elib2Ebook.DomainServices/Getters/GetterBase.cs index 2bb5bd6d..14192673 100644 --- a/src/Core/Logic/Getters/GetterBase.cs +++ b/src/Elib2Ebook.DomainServices/Getters/GetterBase.cs @@ -2,16 +2,16 @@ using System.Net; using System.Text; using System.Web; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; using EpubSharp; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.DomainServices.Getters; public abstract class GetterBase(BookGetterConfig config) : IDisposable { diff --git a/src/Core/Misc/BuilderProvider.cs b/src/Elib2Ebook.DomainServices/Misc/BuilderProvider.cs similarity index 84% rename from src/Core/Misc/BuilderProvider.cs rename to src/Elib2Ebook.DomainServices/Misc/BuilderProvider.cs index 0db5762b..18c69c85 100644 --- a/src/Core/Misc/BuilderProvider.cs +++ b/src/Elib2Ebook.DomainServices/Misc/BuilderProvider.cs @@ -1,8 +1,8 @@ -using Core.Configs; -using Core.Logic.Builders; +using Elib2Ebook.DomainServices.Builders; +using Elib2Ebook.DomainServices.Configs; using Microsoft.Extensions.Logging; -namespace Core.Misc; +namespace Elib2Ebook.DomainServices.Misc; public static class BuilderProvider { diff --git a/src/Elib2Ebook.DomainServices/Misc/GetterFactory.cs b/src/Elib2Ebook.DomainServices/Misc/GetterFactory.cs new file mode 100644 index 00000000..3202df76 --- /dev/null +++ b/src/Elib2Ebook.DomainServices/Misc/GetterFactory.cs @@ -0,0 +1,60 @@ +using System.Reflection; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Getters; + +namespace Elib2Ebook.DomainServices.Misc; + +public static class GetterFactory +{ + private static IEnumerable GetGetterTypes() + { + LoadAllAssemblies(); + + return AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(a => + { + try + { + return a.GetTypes(); + } + catch + { + return []; + } + }) + .Where(myType => myType is { IsClass: true, IsAbstract: false } && myType.IsSubclassOf(typeof(GetterBase))); + } + + private static void LoadAllAssemblies() + { + var loadedAssemblyPaths = AppDomain.CurrentDomain.GetAssemblies() + .Select(a => a.Location) + .Where(p => !string.IsNullOrEmpty(p)) + .ToHashSet(StringComparer.OrdinalIgnoreCase); + + var baseDir = AppDomain.CurrentDomain.BaseDirectory; + + foreach (var dllPath in Directory.EnumerateFiles(baseDir, "Elib2Ebook.ExternalServices.*.dll", SearchOption.AllDirectories)) + { + if (loadedAssemblyPaths.Contains(dllPath)) + continue; + + try + { + Assembly.LoadFrom(dllPath); + } + catch + { + // ignore assemblies that fail to load + } + } + } + + public static GetterBase Get(BookGetterConfig config, Uri url) + { + return GetGetterTypes() + .Select(type => (GetterBase)Activator.CreateInstance(type, config)) + .FirstOrDefault(g => g!.IsSameUrl(url)) ?? + throw new ArgumentException($"Сайт {url.Host} не поддерживается"); + } +} diff --git a/src/Core/Misc/TempFolder/TempFolder.cs b/src/Elib2Ebook.DomainServices/Misc/TempFolder/TempFolder.cs similarity index 91% rename from src/Core/Misc/TempFolder/TempFolder.cs rename to src/Elib2Ebook.DomainServices/Misc/TempFolder/TempFolder.cs index a93f4bfd..4d30e20e 100644 --- a/src/Core/Misc/TempFolder/TempFolder.cs +++ b/src/Elib2Ebook.DomainServices/Misc/TempFolder/TempFolder.cs @@ -1,4 +1,4 @@ -namespace Core.Misc.TempFolder; +namespace Elib2Ebook.DomainServices.Misc.TempFolder; public class TempFolder : IDisposable { diff --git a/src/Core/Misc/TempFolder/TempFolderFactory.cs b/src/Elib2Ebook.DomainServices/Misc/TempFolder/TempFolderFactory.cs similarity index 91% rename from src/Core/Misc/TempFolder/TempFolderFactory.cs rename to src/Elib2Ebook.DomainServices/Misc/TempFolder/TempFolderFactory.cs index b36f7eac..59974a1b 100644 --- a/src/Core/Misc/TempFolder/TempFolderFactory.cs +++ b/src/Elib2Ebook.DomainServices/Misc/TempFolder/TempFolderFactory.cs @@ -1,5 +1,5 @@ #nullable enable -namespace Core.Misc.TempFolder; +namespace Elib2Ebook.DomainServices.Misc.TempFolder; public class TempFolderFactory { diff --git a/src/Core/Patterns/ChapterPattern.xhtml b/src/Elib2Ebook.DomainServices/Patterns/ChapterPattern.xhtml similarity index 100% rename from src/Core/Patterns/ChapterPattern.xhtml rename to src/Elib2Ebook.DomainServices/Patterns/ChapterPattern.xhtml diff --git a/src/Core/Patterns/Roboto-Italic.ttf b/src/Elib2Ebook.DomainServices/Patterns/Roboto-Italic.ttf similarity index 100% rename from src/Core/Patterns/Roboto-Italic.ttf rename to src/Elib2Ebook.DomainServices/Patterns/Roboto-Italic.ttf diff --git a/src/Core/Patterns/Roboto-Regular.ttf b/src/Elib2Ebook.DomainServices/Patterns/Roboto-Regular.ttf similarity index 100% rename from src/Core/Patterns/Roboto-Regular.ttf rename to src/Elib2Ebook.DomainServices/Patterns/Roboto-Regular.ttf diff --git a/src/Core/Patterns/stylesheet.css b/src/Elib2Ebook.DomainServices/Patterns/stylesheet.css similarity index 100% rename from src/Core/Patterns/stylesheet.css rename to src/Elib2Ebook.DomainServices/Patterns/stylesheet.css diff --git a/src/Elib2EbookCli/Elib2EbookCli.csproj b/src/Elib2EbookCli/Elib2EbookCli.csproj index 1d46e38c..51876935 100644 --- a/src/Elib2EbookCli/Elib2EbookCli.csproj +++ b/src/Elib2EbookCli/Elib2EbookCli.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Elib2EbookCli/Program.cs b/src/Elib2EbookCli/Program.cs index 9a6fa6d9..7f7c8cfb 100644 --- a/src/Elib2EbookCli/Program.cs +++ b/src/Elib2EbookCli/Program.cs @@ -2,10 +2,10 @@ using System.Text; using CommandLine; using CommandLine.Text; -using Core.Configs; -using Core.Extensions; -using Core.Logic.Builders; -using Core.Misc; +using Elib2Ebook.DomainServices.Builders; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Misc; using Microsoft.Extensions.Logging; namespace Elib2EbookCli; @@ -54,7 +54,7 @@ await parserResult .WithParsedAsync(async options => { using var getterConfig = BookGetterConfig.GetDefault(options, logger); - using var getter = GetterProvider.Get(getterConfig, options.Url.First().AsUri()); + using var getter = GetterFactory.Get(getterConfig, options.Url.First().AsUri()); try { diff --git a/src/Elib2EbookWeb/Components/BookForm.razor b/src/Elib2EbookWeb/Components/BookForm.razor index f2206e06..900c2f44 100644 --- a/src/Elib2EbookWeb/Components/BookForm.razor +++ b/src/Elib2EbookWeb/Components/BookForm.razor @@ -1,5 +1,5 @@ -@using Core.Configs -@using Core.Extensions +@using Elib2Ebook.DomainServices.Configs +@using Elib2Ebook.DomainServices.Extensions @using Elib2EbookWeb.Misc @inject IJSRuntime JsRuntime @inject ISnackbar Snackbar @@ -97,7 +97,8 @@ private InputType _passwordInput = InputType.Password; private string _passwordInputIcon = Icons.Material.Filled.VisibilityOff; - [Parameter] public required Func OnClick { get; set; } + [Parameter] + public required Func OnClick { get; set; } void ShowPassword() { diff --git a/src/Elib2EbookWeb/Components/Pages/Error.razor b/src/Elib2EbookWeb/Components/Pages/Error.razor index bae7cf02..fd81a455 100644 --- a/src/Elib2EbookWeb/Components/Pages/Error.razor +++ b/src/Elib2EbookWeb/Components/Pages/Error.razor @@ -28,7 +28,8 @@ @code{ - [CascadingParameter] private HttpContext HttpContext { get; set; } + [CascadingParameter] + private HttpContext HttpContext { get; set; } private string RequestId { get; set; } private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); diff --git a/src/Elib2EbookWeb/Components/Pages/Home.razor b/src/Elib2EbookWeb/Components/Pages/Home.razor index 372132a1..a25f9228 100644 --- a/src/Elib2EbookWeb/Components/Pages/Home.razor +++ b/src/Elib2EbookWeb/Components/Pages/Home.razor @@ -1,9 +1,9 @@ @page "/" -@using Core.Configs -@using Core.Extensions -@using Core.Misc -@using Core.Misc.TempFolder -@using Core.Types.Book +@using Elib2Ebook.Domain.Book +@using Elib2Ebook.DomainServices.Configs +@using Elib2Ebook.DomainServices.Extensions +@using Elib2Ebook.DomainServices.Misc +@using Elib2Ebook.DomainServices.Misc.TempFolder @rendermode InteractiveServer @inject IJSRuntime JsRuntime @inject ISnackbar Snackbar @@ -39,7 +39,7 @@ try { using var getterConfig = BookGetterConfig.GetDefault(options, logger); - using var getter = GetterProvider.Get(getterConfig, options.Url.First().AsUri()); + using var getter = GetterFactory.Get(getterConfig, options.Url.First().AsUri()); await getter.Init(); await getter.Authorize(); diff --git a/src/Elib2EbookWeb/Components/ShortBook.razor b/src/Elib2EbookWeb/Components/ShortBook.razor index 2f85581c..615f6835 100644 --- a/src/Elib2EbookWeb/Components/ShortBook.razor +++ b/src/Elib2EbookWeb/Components/ShortBook.razor @@ -1,4 +1,4 @@ -@using Core.Types.Book +@using Elib2Ebook.Domain.Book @@ -50,7 +50,9 @@ @code { - [Parameter] public required Book Book { get; set; } + + [Parameter] + public required Book Book { get; set; } private string _cover; diff --git a/src/Elib2EbookWeb/Elib2EbookWeb.csproj b/src/Elib2EbookWeb/Elib2EbookWeb.csproj index 41d88c41..33cc3c98 100644 --- a/src/Elib2EbookWeb/Elib2EbookWeb.csproj +++ b/src/Elib2EbookWeb/Elib2EbookWeb.csproj @@ -4,7 +4,7 @@ - + diff --git a/src/Core/Logic/Getters/AcomicsGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Acomics/AcomicsGetter.cs similarity index 92% rename from src/Core/Logic/Getters/AcomicsGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Acomics/AcomicsGetter.cs index 4d8b2a6a..5f68fee8 100644 --- a/src/Core/Logic/Getters/AcomicsGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Acomics/AcomicsGetter.cs @@ -1,13 +1,14 @@ using System.Net; using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Acomics; public class AcomicsGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Acomics/Elib2Ebook.ExternalServices.Acomics.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Acomics/Elib2Ebook.ExternalServices.Acomics.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Acomics/Elib2Ebook.ExternalServices.Acomics.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/AuthorTodayGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/AuthorTodayGetter.cs similarity index 90% rename from src/Core/Logic/Getters/AuthorTodayGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/AuthorTodayGetter.cs index 73549b3f..18f13907 100644 --- a/src/Core/Logic/Getters/AuthorTodayGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/AuthorTodayGetter.cs @@ -3,15 +3,15 @@ using System.Net.Http.Json; using System.Security.Cryptography; using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Misc; -using Core.Types.AuthorToday; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.AuthorToday.Types; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.AuthorToday; public class AuthorTodayGetter(BookGetterConfig config) : GetterBase(config) { @@ -80,7 +80,7 @@ public override async Task Authorize() Config.Options.Login, Config.Options.Password }))); - var data = await response.Content.ReadFromJsonAsync(); + var data = await response.Content.ReadFromJsonAsync(); if (!string.IsNullOrWhiteSpace(data?.Token)) { @@ -88,7 +88,7 @@ public override async Task Authorize() Config.Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", data.Token); response = await Config.Client.SendWithTriesAsync(() => GetDefaultMessage(ApiUrl.MakeRelativeUri("/v1/account/current-user"), _apiUrl)); - var user = await response.Content.ReadFromJsonAsync(); + var user = await response.Content.ReadFromJsonAsync(); UserId = user!.Id.ToString(); } else @@ -118,7 +118,7 @@ public override async Task Get(Uri url) return book; } - private async Task FillAdditional(Book book, AuthorTodayBookDetails details) + private async Task FillAdditional(Book book, BookDetails details) { if (!Config.Options.HasAdditionalType(AdditionalTypeEnum.Images) || details.GalleryImages == null || details.GalleryImages.Length == 0) { @@ -150,7 +150,7 @@ private HttpRequestMessage GetDefaultMessage(Uri uri, Uri host, HttpContent cont return message; } - private async Task GetBookDetails(string bookId) + private async Task GetBookDetails(string bookId) { var response = await Config.Client.SendWithTriesAsync(() => GetDefaultMessage(ApiUrl.MakeRelativeUri($"/v1/work/{bookId}/details"), _apiUrl)); if (response.StatusCode != HttpStatusCode.OK) @@ -158,15 +158,15 @@ private async Task GetBookDetails(string bookId) throw new Exception("Книга не найдена"); } - return await response.Content.ReadFromJsonAsync(); + return await response.Content.ReadFromJsonAsync(); } - private Author GetAuthor(AuthorTodayBookDetails book) + private Author GetAuthor(BookDetails book) { return new Author(book.AuthorFio, SystemUrl.MakeRelativeUri($"/u/{book.AuthorUserName}/works")); } - private IEnumerable GetCoAuthors(AuthorTodayBookDetails book) + private IEnumerable GetCoAuthors(BookDetails book) { var result = new List(); if (!string.IsNullOrWhiteSpace(book.CoAuthorFio)) @@ -177,7 +177,7 @@ private IEnumerable GetCoAuthors(AuthorTodayBookDetails book) return result; } - private Seria GetSeria(AuthorTodayBookDetails book) + private Seria GetSeria(BookDetails book) { if (!book.SeriesId.HasValue) { @@ -192,7 +192,7 @@ private Seria GetSeria(AuthorTodayBookDetails book) }; } - private Task GetCover(AuthorTodayBookDetails book) + private Task GetCover(BookDetails book) { return !string.IsNullOrWhiteSpace(book.CoverUrl) ? SaveImage(book.CoverUrl.AsUri()) : Task.FromResult(default(TempFile)); } @@ -207,7 +207,7 @@ protected override HttpRequestMessage GetImageRequestMessage(Uri uri) return base.GetImageRequestMessage(uri); } - private async Task> FillChapters(AuthorTodayBookDetails book) + private async Task> FillChapters(BookDetails book) { var result = new List(); if (Config.Options.NoChapters) @@ -238,7 +238,7 @@ private async Task> FillChapters(AuthorTodayBookDetails boo return result; } - private async Task> GetChapters(AuthorTodayBookDetails book) + private async Task> GetChapters(BookDetails book) { var result = new List(); diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Elib2Ebook.ExternalServices.AuthorToday.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Elib2Ebook.ExternalServices.AuthorToday.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Elib2Ebook.ExternalServices.AuthorToday.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Types/AuthorToday/AuthorTodayAuthResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/AuthResponse.cs similarity index 68% rename from src/Core/Types/AuthorToday/AuthorTodayAuthResponse.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/AuthResponse.cs index 5be99357..49165fc3 100644 --- a/src/Core/Types/AuthorToday/AuthorTodayAuthResponse.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/AuthResponse.cs @@ -1,8 +1,8 @@ using System.Text.Json.Serialization; -namespace Core.Types.AuthorToday; +namespace Elib2Ebook.ExternalServices.AuthorToday.Types; -public class AuthorTodayAuthResponse +internal class AuthResponse { [JsonPropertyName("message")] public string Message { get; set; } diff --git a/src/Core/Types/AuthorToday/AuthorTodayChapter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/AuthorTodayChapter.cs similarity index 57% rename from src/Core/Types/AuthorToday/AuthorTodayChapter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/AuthorTodayChapter.cs index b0a3ff0d..70f417fc 100644 --- a/src/Core/Types/AuthorToday/AuthorTodayChapter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/AuthorTodayChapter.cs @@ -2,25 +2,33 @@ using System.Text; using System.Text.Json.Serialization; -namespace Core.Types.AuthorToday; +namespace Elib2Ebook.ExternalServices.AuthorToday.Types; -public class AuthorTodayChapter +internal class AuthorTodayChapter { - [JsonPropertyName("id")] public long Id { get; set; } + [JsonPropertyName("id")] + public long Id { get; set; } - [JsonPropertyName("title")] public string Title { get; set; } + [JsonPropertyName("title")] + public string Title { get; set; } - [JsonPropertyName("sortOrder")] public long SortOrder { get; set; } + [JsonPropertyName("sortOrder")] + public long SortOrder { get; set; } - [JsonPropertyName("text")] public string Text { get; set; } + [JsonPropertyName("text")] + public string Text { get; set; } - [JsonPropertyName("key")] public string Key { get; set; } + [JsonPropertyName("key")] + public string Key { get; set; } - [JsonPropertyName("IsSuccessful")] public bool IsSuccessful { get; set; } + [JsonPropertyName("IsSuccessful")] + public bool IsSuccessful { get; set; } - [JsonPropertyName("IsDraft")] public bool IsDraft { get; set; } + [JsonPropertyName("IsDraft")] + public bool IsDraft { get; set; } - [JsonPropertyName("code")] public string Code { get; set; } + [JsonPropertyName("code")] + public string Code { get; set; } public string Decode(string userId, string cert) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/BookDetails.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/BookDetails.cs new file mode 100644 index 00000000..8d84696e --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/BookDetails.cs @@ -0,0 +1,45 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.AuthorToday.Types; + +internal class BookDetails +{ + [JsonPropertyName("annotation")] + public string Annotation { get; set; } + + [JsonPropertyName("seriesId")] + public long? SeriesId { get; set; } + + [JsonPropertyName("seriesWorkNumber")] + public long? SeriesWorkNumber { get; set; } + + [JsonPropertyName("seriesTitle")] + public string SeriesTitle { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("authorFIO")] + public string AuthorFio { get; set; } + + [JsonPropertyName("coAuthorFIO")] + public string CoAuthorFio { get; set; } + + [JsonPropertyName("coverUrl")] + public string CoverUrl { get; set; } + + [JsonPropertyName("chapters")] + public AuthorTodayChapter[] Chapters { get; set; } + + [JsonPropertyName("id")] + public long Id { get; set; } + + [JsonPropertyName("authorUserName")] + public string AuthorUserName { get; set; } + + [JsonPropertyName("CoAuthorUserName")] + public string CoAuthorUserName { get; set; } + + [JsonPropertyName("galleryImages")] + public Image[] GalleryImages { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/Image.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/Image.cs new file mode 100644 index 00000000..597f541f --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/Image.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.AuthorToday.Types; + +internal class Image +{ + [JsonPropertyName("url")] + public string Url { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/User.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/User.cs new file mode 100644 index 00000000..12624ec2 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.AuthorToday/Types/User.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.AuthorToday.Types; + +internal class User +{ + [JsonPropertyName("id")] + public long Id { get; set; } +} diff --git a/src/Core/Logic/Getters/BookInBookGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BookInBook/BookInBookGetter.cs similarity index 94% rename from src/Core/Logic/Getters/BookInBookGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.BookInBook/BookInBookGetter.cs index aa101f7e..1d20e202 100644 --- a/src/Core/Logic/Getters/BookInBookGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BookInBook/BookInBookGetter.cs @@ -1,13 +1,14 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.BookInBook; public class BookInBookGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BookInBook/Elib2Ebook.ExternalServices.BookInBook.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.BookInBook/Elib2Ebook.ExternalServices.BookInBook.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BookInBook/Elib2Ebook.ExternalServices.BookInBook.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/BookRiverGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/BookRiverGetter.cs similarity index 91% rename from src/Core/Logic/Getters/BookRiverGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/BookRiverGetter.cs index 78a986f5..9127eeff 100644 --- a/src/Core/Logic/Getters/BookRiverGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/BookRiverGetter.cs @@ -2,16 +2,17 @@ using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Bookriver; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.BookRiver.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.BookRiver; public class BookriverGetter(BookGetterConfig config) : GetterBase(config) { @@ -37,7 +38,7 @@ public override async Task Authorize() }; using var post = await Config.Client.PostAsJsonAsync(_apiUrl.MakeRelativeUri("/api/v1/auth/login"), payload); - var data = await post.Content.ReadFromJsonAsync(); + var data = await post.Content.ReadFromJsonAsync(); if (!string.IsNullOrWhiteSpace(data.Token)) { Config.Logger.LogInformation("Успешно авторизовались"); @@ -172,14 +173,14 @@ private async Task GetChapter(long bookChapterId) } var content = await response.Content.ReadAsStringAsync(); - return content.Deserialize>().Data.Content.AsHtmlDoc(); + return content.Deserialize>().Data.Content.AsHtmlDoc(); } private async Task> GetToc(string bookId) { var response = await Config.Client.GetWithTriesAsync(_apiUrl.MakeRelativeUri($"/api/v1/books/chapters/text/published?bookId={bookId}")); var content = await response.Content.ReadAsStringAsync(); - return SliceToc(content.Deserialize>().Data, c => c.Name); + return SliceToc(content.Deserialize>().Data, c => c.Name); } private Task GetCover(HtmlDocument doc, Uri uri) diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Elib2Ebook.ExternalServices.BookRiver.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Elib2Ebook.ExternalServices.BookRiver.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Elib2Ebook.ExternalServices.BookRiver.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/ApiResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/ApiResponse.cs new file mode 100644 index 00000000..447e88a8 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/ApiResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BookRiver.Types; + +internal class ApiResponse +{ + [JsonPropertyName("data")] + public T Data { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/AuthResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/AuthResponse.cs new file mode 100644 index 00000000..edaf131d --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/AuthResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BookRiver.Types; + +internal class AuthResponse +{ + [JsonPropertyName("token")] + public string Token { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/BookRiverBook.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/BookRiverBook.cs new file mode 100644 index 00000000..bbea5ec3 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/BookRiverBook.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BookRiver.Types; + +internal class BookRiverBook +{ + [JsonPropertyName("currentGraphqlBook")] + public BookRiverCurrentBook Book { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/BookRiverChapter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/BookRiverChapter.cs new file mode 100644 index 00000000..83c350b2 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/BookRiverChapter.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BookRiver.Types; + +internal class BookRiverChapter +{ + [JsonPropertyName("id")] + public long Id { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/BookRiverCurrentBook.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/BookRiverCurrentBook.cs new file mode 100644 index 00000000..49290ac1 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/BookRiverCurrentBook.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BookRiver.Types; + +internal class BookRiverCurrentBook +{ + [JsonPropertyName("id")] + public long Id { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/ChapterContent.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/ChapterContent.cs new file mode 100644 index 00000000..27d913c5 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BookRiver/Types/ChapterContent.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BookRiver.Types; + +internal class ChapterContent +{ + [JsonPropertyName("content")] + public string Content { get; set; } +} diff --git a/src/Core/Logic/Getters/BookinistGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookinist/BookinistGetter.cs similarity index 90% rename from src/Core/Logic/Getters/BookinistGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Bookinist/BookinistGetter.cs index 3fd50ebe..654bdb49 100644 --- a/src/Core/Logic/Getters/BookinistGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookinist/BookinistGetter.cs @@ -1,12 +1,13 @@ -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Bookinist; public class BookinistGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Bookinist/Elib2Ebook.ExternalServices.Bookinist.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookinist/Elib2Ebook.ExternalServices.Bookinist.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookinist/Elib2Ebook.ExternalServices.Bookinist.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Elib2Ebook.ExternalServices.BooksYandex.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Elib2Ebook.ExternalServices.BooksYandex.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Elib2Ebook.ExternalServices.BooksYandex.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/BooksYandex/BooksYandexBooksGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Getters/BooksYandexBooksGetter.cs similarity index 95% rename from src/Core/Logic/Getters/BooksYandex/BooksYandexBooksGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Getters/BooksYandexBooksGetter.cs index 780931cc..4e47f775 100644 --- a/src/Core/Logic/Getters/BooksYandex/BooksYandexBooksGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Getters/BooksYandexBooksGetter.cs @@ -1,14 +1,13 @@ using System.Net; using System.Net.Http.Json; -using Core.Configs; -using Core.Extensions; -using Core.Misc; -using Core.Types.Book; -using Core.Types.BookYandex; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.ExternalServices.BooksYandex.Types; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.BooksYandex; +namespace Elib2Ebook.ExternalServices.BooksYandex.Getters; public class BooksYandexBooksGetter(BookGetterConfig config) : BooksYandexGetterBase(config) { diff --git a/src/Core/Logic/Getters/BooksYandex/BooksYandexComicsGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Getters/BooksYandexComicsGetter.cs similarity index 82% rename from src/Core/Logic/Getters/BooksYandex/BooksYandexComicsGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Getters/BooksYandexComicsGetter.cs index c521e033..da0e3491 100644 --- a/src/Core/Logic/Getters/BooksYandex/BooksYandexComicsGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Getters/BooksYandexComicsGetter.cs @@ -1,11 +1,11 @@ using System.Net.Http.Json; using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.BookYandex; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.ExternalServices.BooksYandex.Types; -namespace Core.Logic.Getters.BooksYandex; +namespace Elib2Ebook.ExternalServices.BooksYandex.Getters; public class BooksYandexComicsGetter(BookGetterConfig config) : BooksYandexGetterBase(config) { diff --git a/src/Core/Logic/Getters/BooksYandex/BooksYandexGetterBase.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Getters/BooksYandexGetterBase.cs similarity index 92% rename from src/Core/Logic/Getters/BooksYandex/BooksYandexGetterBase.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Getters/BooksYandexGetterBase.cs index 9578ea3c..71ad4a37 100644 --- a/src/Core/Logic/Getters/BooksYandex/BooksYandexGetterBase.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Getters/BooksYandexGetterBase.cs @@ -1,12 +1,13 @@ using System.Net; using System.Net.Http.Json; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.BookYandex; -using Core.Types.Common; - -namespace Core.Logic.Getters.BooksYandex; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.BooksYandex.Types; + +namespace Elib2Ebook.ExternalServices.BooksYandex.Getters; public abstract class BooksYandexGetterBase(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexAudioBitrate.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexAudioBitrate.cs new file mode 100644 index 00000000..b4e556ef --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexAudioBitrate.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BooksYandex.Types; + +internal class BooksYandexAudioBitrate +{ + [JsonPropertyName("url")] + public string Url { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexAudioOffline.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexAudioOffline.cs new file mode 100644 index 00000000..aa002924 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexAudioOffline.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BooksYandex.Types; + +internal class BooksYandexAudioOffline +{ + [JsonPropertyName("max_bit_rate")] + public BooksYandexAudioBitrate Max { get; set; } + + [JsonPropertyName("min_bit_rate")] + public BooksYandexAudioBitrate Min { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexAudioTrack.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexAudioTrack.cs new file mode 100644 index 00000000..09282bc0 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexAudioTrack.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BooksYandex.Types; + +internal class BooksYandexAudioTrack +{ + [JsonPropertyName("offline")] + public BooksYandexAudioOffline Offline { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexAuthor.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexAuthor.cs new file mode 100644 index 00000000..991057e5 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexAuthor.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BooksYandex.Types; + +public class BooksYandexAuthor +{ + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("uuid")] + public string Uuid { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexBook.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexBook.cs new file mode 100644 index 00000000..cbf80d1c --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexBook.cs @@ -0,0 +1,54 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BooksYandex.Types; + +public abstract class BookmateBookBase +{ + [JsonPropertyName("uuid")] + public string UUID { get; set; } + + [JsonPropertyName("annotation")] + public string Annotation { get; set; } + + [JsonPropertyName("cover")] + public BooksYandexCover Cover { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("language")] + public string Language { get; set; } + + [JsonPropertyName("linked_book_uuids")] + public string[] LinkedBooks { get; set; } + + [JsonPropertyName("linked_audiobook_uuids")] + public string[] LinkedAudio { get; set; } + + [JsonPropertyName("source_type")] + public string SourceType { get; set; } + + public abstract BooksYandexAuthor GetAuthor(); +} + +public class BooksYandexBook : BookmateBookBase +{ + [JsonPropertyName("authors_objects")] + public BooksYandexAuthor[] AuthorsObjects { get; set; } + + public override BooksYandexAuthor GetAuthor() + { + return AuthorsObjects?.FirstOrDefault(); + } +} + +public class BookmateComic : BookmateBookBase +{ + [JsonPropertyName("authors")] + public BooksYandexAuthor[] Authors { get; set; } + + public override BooksYandexAuthor GetAuthor() + { + return Authors?.FirstOrDefault(); + } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexComicContent.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexComicContent.cs new file mode 100644 index 00000000..5c3d3d00 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexComicContent.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BooksYandex.Types; + +internal class BooksYandexComicContent +{ + [JsonPropertyName("uris")] + public BooksYandexUri Uri { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexComicMetadata.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexComicMetadata.cs new file mode 100644 index 00000000..a7f0eb7b --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexComicMetadata.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BooksYandex.Types; + +internal class BooksYandexComicMetadata +{ + [JsonPropertyName("pages")] + public BooksYandexComicPage[] Pages { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexComicPage.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexComicPage.cs new file mode 100644 index 00000000..80d87b96 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexComicPage.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BooksYandex.Types; + +internal class BooksYandexComicPage +{ + [JsonPropertyName("content")] + public BooksYandexComicContent Content { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexCover.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexCover.cs new file mode 100644 index 00000000..f2b4e730 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexCover.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BooksYandex.Types; + +public class BooksYandexCover +{ + [JsonPropertyName("large")] + public string Large { get; set; } + + [JsonPropertyName("small")] + public string Small { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexEpisode.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexEpisode.cs new file mode 100644 index 00000000..9e29c626 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexEpisode.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BooksYandex.Types; + +internal class BooksYandexEpisode +{ + [JsonPropertyName("uuid")] + public string Uuid { get; set; } + + [JsonPropertyName("can_be_read")] + public bool CanBeRead { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexEpisodes.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexEpisodes.cs new file mode 100644 index 00000000..e39cb51c --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexEpisodes.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BooksYandex.Types; + +internal class BooksYandexEpisodes +{ + [JsonPropertyName("episodes")] + public BooksYandexEpisode[] Episodes { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexPlaylist.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexPlaylist.cs new file mode 100644 index 00000000..55278e8f --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexPlaylist.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BooksYandex.Types; + +internal class BooksYandexPlaylist +{ + [JsonPropertyName("tracks")] + public BooksYandexAudioTrack[] Tracks { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexResponse.cs new file mode 100644 index 00000000..024e8995 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexResponse.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BooksYandex.Types; + +public class BooksYandexResponse +{ + [JsonPropertyName("book")] + public BooksYandexBook Book { get; set; } + + [JsonPropertyName("audiobook")] + public BooksYandexBook AudioBook { get; set; } + + [JsonPropertyName("comicbook")] + public BookmateComic Comicbook { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexUri.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexUri.cs new file mode 100644 index 00000000..177b79ff --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.BooksYandex/Types/BooksYandexUri.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.BooksYandex.Types; + +internal class BooksYandexUri +{ + [JsonPropertyName("Image")] + public string Image { get; set; } + + [JsonPropertyName("path")] + public string Path { get; set; } +} diff --git a/src/Core/Logic/Getters/BookstabGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/BookstabGetter.cs similarity index 84% rename from src/Core/Logic/Getters/BookstabGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/BookstabGetter.cs index b775c560..8016de19 100644 --- a/src/Core/Logic/Getters/BookstabGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/BookstabGetter.cs @@ -1,14 +1,15 @@ using System.Net; using System.Net.Http.Json; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Bookstab; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.Bookstab.Types; using HtmlAgilityPack; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Bookstab; public class BookstabGetter(BookGetterConfig config) : GetterBase(config) { @@ -24,7 +25,7 @@ public override async Task Get(Uri url) var bookId = GetId(url); url = SystemUrl.MakeRelativeUri($"/book/{bookId}"); using var response = await Config.Client.GetWithTriesAsync(_apiUrl.MakeRelativeUri($"/api/reader-get/{bookId}")); - var data = await response.Content.ReadFromJsonAsync(); + var data = await response.Content.ReadFromJsonAsync(); var book = new Book(url) { @@ -38,7 +39,7 @@ public override async Task Get(Uri url) return book; } - private Author GetAuthor(BookstabApiResponse book) + private Author GetAuthor(ApiResponse book) { return new Author(book.Book.User.Pseudonym, SystemUrl.MakeRelativeUri($"/user/{book.Book.User.Name}")); } @@ -50,7 +51,7 @@ private static string GetAnnotation(BookstabBook book) string.Join("", book.Excerpt.Split("\n", StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim().CoverTag("p"))); } - private async Task> FillChapters(BookstabApiResponse response, Uri uri, string bookId) + private async Task> FillChapters(ApiResponse response, Uri uri, string bookId) { var result = new List(); if (Config.Options.NoChapters) @@ -93,12 +94,12 @@ private async Task GetChapter(int chapterId, string bookId) continue; } - var data = await response.Content.ReadFromJsonAsync(); + var data = await response.Content.ReadFromJsonAsync(); return string.IsNullOrWhiteSpace(data?.Chapter.Body) ? null : data.Chapter.Body.AsHtmlDoc(); } } - private Task GetCover(BookstabApiResponse response) + private Task GetCover(ApiResponse response) { var imagePath = response.Book.Image; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(_apiUrl.MakeRelativeUri($"/storage/{imagePath}")) : Task.FromResult(default(TempFile)); diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Elib2Ebook.ExternalServices.Bookstab.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Elib2Ebook.ExternalServices.Bookstab.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Elib2Ebook.ExternalServices.Bookstab.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Types/ApiResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Types/ApiResponse.cs new file mode 100644 index 00000000..f3227c63 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Types/ApiResponse.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Bookstab.Types; + +internal class ApiResponse +{ + [JsonPropertyName("book")] + public BookstabBook Book { get; set; } + + [JsonPropertyName("chapter")] + public BookstabChapter Chapter { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Types/BookstabBook.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Types/BookstabBook.cs new file mode 100644 index 00000000..e5a96f5b --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Types/BookstabBook.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Bookstab.Types; + +internal class BookstabBook +{ + [JsonPropertyName("image")] + public string Image { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("excerpt")] + public string Excerpt { get; set; } + + [JsonPropertyName("user")] + public BooksnabUser User { get; set; } + + [JsonPropertyName("chapters_show")] + public BookstabChapter[] ChaptersShow { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Types/BookstabChapter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Types/BookstabChapter.cs new file mode 100644 index 00000000..136634e4 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Types/BookstabChapter.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Bookstab.Types; + +internal class BookstabChapter +{ + [JsonPropertyName("id")] + public int Id { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("body")] + public string Body { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Types/User.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Types/User.cs new file mode 100644 index 00000000..94912b5f --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstab/Types/User.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Bookstab.Types; + +internal class BooksnabUser +{ + [JsonPropertyName("pseudonym")] + public string Pseudonym { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } +} diff --git a/src/Core/Logic/Getters/BookstimeGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstime/BookstimeGetter.cs similarity index 93% rename from src/Core/Logic/Getters/BookstimeGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Bookstime/BookstimeGetter.cs index 2e9f8054..1532295b 100644 --- a/src/Core/Logic/Getters/BookstimeGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstime/BookstimeGetter.cs @@ -1,16 +1,17 @@ using System.Net; using System.Net.Http.Json; using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Bookstime; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.Bookstime.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Bookstime; public class BookstimeGetter(BookGetterConfig config) : GetterBase(config) { @@ -43,7 +44,7 @@ public override async Task Authorize() throw new Exception($"Не удалось авторизоваться. {content}"); } - var response = await post.Content.ReadFromJsonAsync(); + var response = await post.Content.ReadFromJsonAsync(); if (string.IsNullOrWhiteSpace(response.XOctoberErrorMessage)) { Config.Logger.LogInformation("Успешно авторизовались"); diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstime/Elib2Ebook.ExternalServices.Bookstime.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstime/Elib2Ebook.ExternalServices.Bookstime.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstime/Elib2Ebook.ExternalServices.Bookstime.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstime/Types/AuthResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstime/Types/AuthResponse.cs new file mode 100644 index 00000000..ae5c2d61 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookstime/Types/AuthResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Bookstime.Types; + +internal class AuthResponse +{ + [JsonPropertyName("X_OCTOBER_ERROR_MESSAGE")] + public string XOctoberErrorMessage { get; set; } +} diff --git a/src/Core/Logic/Getters/BookurukGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookuruk/BookurukGetter.cs similarity index 95% rename from src/Core/Logic/Getters/BookurukGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Bookuruk/BookurukGetter.cs index 6115f799..ca37a923 100644 --- a/src/Core/Logic/Getters/BookurukGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookuruk/BookurukGetter.cs @@ -1,12 +1,13 @@ -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Bookuruk; public class BookurukGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Bookuruk/Elib2Ebook.ExternalServices.Bookuruk.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookuruk/Elib2Ebook.ExternalServices.Bookuruk.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Bookuruk/Elib2Ebook.ExternalServices.Bookuruk.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/BoostyGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/BoostyGetter.cs similarity index 93% rename from src/Core/Logic/Getters/BoostyGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/BoostyGetter.cs index ca89ddad..8bbd2ba1 100644 --- a/src/Core/Logic/Getters/BoostyGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/BoostyGetter.cs @@ -1,14 +1,15 @@ using System.Net.Http.Json; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Boosty; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.Boosty.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Boosty; public class BoostyGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/Elib2Ebook.ExternalServices.Boosty.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/Elib2Ebook.ExternalServices.Boosty.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/Elib2Ebook.ExternalServices.Boosty.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/Types/BoostyApiResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/Types/BoostyApiResponse.cs new file mode 100644 index 00000000..4825f7c0 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/Types/BoostyApiResponse.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Boosty.Types; + +internal class BoostyApiResponse +{ + [JsonPropertyName("data")] + public T Data { get; set; } + + [JsonPropertyName("extra")] + public BoostyExtra Extra { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/Types/BoostyExtra.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/Types/BoostyExtra.cs new file mode 100644 index 00000000..ac2b641a --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/Types/BoostyExtra.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Boosty.Types; + +internal class BoostyExtra +{ + [JsonPropertyName("isLast")] + public bool IsLast { get; set; } + + [JsonPropertyName("offset")] + public string Offset { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/Types/BoostyPost.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/Types/BoostyPost.cs new file mode 100644 index 00000000..d411cf34 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Boosty/Types/BoostyPost.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Boosty.Types; + +internal class BoostyPost +{ + [JsonPropertyName("id")] + public Guid Id { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("hasAccess")] + public bool HasAccess { get; set; } +} diff --git a/src/Core/Logic/Getters/BoovelGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Boovel/BoovelGetter.cs similarity index 93% rename from src/Core/Logic/Getters/BoovelGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Boovel/BoovelGetter.cs index 620cbb72..582b0d8c 100644 --- a/src/Core/Logic/Getters/BoovelGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Boovel/BoovelGetter.cs @@ -1,14 +1,15 @@ using System.Security.Cryptography; using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Boovel; public class BoovelGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Boovel/Elib2Ebook.ExternalServices.Boovel.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Boovel/Elib2Ebook.ExternalServices.Boovel.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Boovel/Elib2Ebook.ExternalServices.Boovel.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/DarkNovelsGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/DarkNovelsGetter.cs similarity index 95% rename from src/Core/Logic/Getters/DarkNovelsGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/DarkNovelsGetter.cs index f7a1351e..697c052d 100644 --- a/src/Core/Logic/Getters/DarkNovelsGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/DarkNovelsGetter.cs @@ -3,16 +3,17 @@ using System.Net.Http.Json; using System.Text; using System.Text.RegularExpressions; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.DarkNovels; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.DarkNovels.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.DarkNovels; public class DarkNovelsGetter : GetterBase { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Elib2Ebook.ExternalServices.DarkNovels.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Elib2Ebook.ExternalServices.DarkNovels.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Elib2Ebook.ExternalServices.DarkNovels.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Types/DarkNovelsAuthData.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Types/DarkNovelsAuthData.cs new file mode 100644 index 00000000..b94ab2cf --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Types/DarkNovelsAuthData.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.DarkNovels.Types; + +internal class DarkNovelsAuthResponse +{ + [JsonPropertyName("token")] + public DarkNovelsAuthToken Token { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Types/DarkNovelsAuthToken.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Types/DarkNovelsAuthToken.cs new file mode 100644 index 00000000..ef4db6a7 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Types/DarkNovelsAuthToken.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.DarkNovels.Types; + +internal class DarkNovelsAuthToken +{ + [JsonPropertyName("accessToken")] + public string AccessToken { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Types/DarkNovelsChapter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Types/DarkNovelsChapter.cs new file mode 100644 index 00000000..48892852 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Types/DarkNovelsChapter.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.DarkNovels.Types; + +internal class DarkNovelsChapter +{ + [JsonPropertyName("id")] + public int Id { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("payed")] + public int Payed { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Types/DarkNovelsData.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Types/DarkNovelsData.cs new file mode 100644 index 00000000..30259262 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.DarkNovels/Types/DarkNovelsData.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.DarkNovels.Types; + +internal class DarkNovelsData +{ + [JsonPropertyName("status")] + public string Status { get; set; } + + [JsonPropertyName("message")] + public string Message { get; set; } + + [JsonPropertyName("data")] + public T Data { get; set; } +} diff --git a/src/Core/Logic/Getters/DesuGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Desu/DesuGetter.cs similarity index 94% rename from src/Core/Logic/Getters/DesuGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Desu/DesuGetter.cs index eca41fca..1d27fc2e 100644 --- a/src/Core/Logic/Getters/DesuGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Desu/DesuGetter.cs @@ -1,15 +1,16 @@ using System.Text; using System.Text.Json; using System.Text.RegularExpressions; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.DomainServices.Extensions; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Desu; public class DesuGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Desu/Elib2Ebook.ExternalServices.Desu.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Desu/Elib2Ebook.ExternalServices.Desu.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Desu/Elib2Ebook.ExternalServices.Desu.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/DreameGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/DreameGetter.cs similarity index 92% rename from src/Core/Logic/Getters/DreameGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/DreameGetter.cs index 469d2939..9d3dff45 100644 --- a/src/Core/Logic/Getters/DreameGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/DreameGetter.cs @@ -1,15 +1,16 @@ using System.Text; using System.Text.Json; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.Dreame; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.Dreame.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Dreame; public class DreameGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Elib2Ebook.ExternalServices.Dreame.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Elib2Ebook.ExternalServices.Dreame.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Elib2Ebook.ExternalServices.Dreame.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreameApiResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreameApiResponse.cs new file mode 100644 index 00000000..25584465 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreameApiResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Dreame.Types; + +internal class DreameApiResponse +{ + [JsonPropertyName("data")] + public T Data { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreameCatalog.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreameCatalog.cs new file mode 100644 index 00000000..43640df5 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreameCatalog.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Dreame.Types; + +internal class DreameCatalog +{ + [JsonPropertyName("pager")] + public DreamePager Pager { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreameChapter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreameChapter.cs new file mode 100644 index 00000000..a76be5a4 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreameChapter.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Dreame.Types; + +internal class DreameChapter +{ + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("id")] + public string Id { get; set; } + + [JsonPropertyName("content")] + public string Content { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreameNovel.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreameNovel.cs new file mode 100644 index 00000000..a978e94c --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreameNovel.cs @@ -0,0 +1,27 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Dreame.Types; + +internal class DreameNovel +{ + [JsonPropertyName("id")] + public long Id { get; set; } + + [JsonPropertyName("author_id")] + public string AuthorId { get; set; } + + [JsonPropertyName("author_name")] + public string AuthorName { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("descr")] + public string Description { get; set; } + + [JsonPropertyName("language")] + public string Language { get; set; } + + [JsonPropertyName("cover_url")] + public string Cover { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreamePager.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreamePager.cs new file mode 100644 index 00000000..b511d2f0 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Dreame/Types/DreamePager.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Dreame.Types; + +internal class DreamePager +{ + [JsonPropertyName("chap_list")] + public List ChapterList { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Elib2Ebook.ExternalServices.FanFicus.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Elib2Ebook.ExternalServices.FanFicus.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Elib2Ebook.ExternalServices.FanFicus.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/FanFicusGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/FanFicusGetter.cs similarity index 93% rename from src/Core/Logic/Getters/FanFicusGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/FanFicusGetter.cs index c518327d..fea1182d 100644 --- a/src/Core/Logic/Getters/FanFicusGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/FanFicusGetter.cs @@ -1,15 +1,16 @@ using System.Net; using System.Net.Http.Headers; using System.Net.Http.Json; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.FanFicus; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.FanFicus.Types; using HtmlAgilityPack; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.FanFicus; public class FanFicusGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusApiResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusApiResponse.cs new file mode 100644 index 00000000..57295940 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusApiResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.FanFicus.Types; + +internal class FanFicusApiResponse +{ + [JsonPropertyName("value")] + public T Value { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusBook.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusBook.cs new file mode 100644 index 00000000..38ba0fd9 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusBook.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.FanFicus.Types; + +internal class FanFicusBook +{ + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("description")] + public string Description { get; set; } + + [JsonPropertyName("creatorId")] + public FanFicusCreator[] Creators { get; set; } + + [JsonPropertyName("images")] + public FanFicusImage[] Images { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusChapter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusChapter.cs new file mode 100644 index 00000000..0d6a978a --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusChapter.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.FanFicus.Types; + +internal class FanFicusChapter +{ + [JsonPropertyName("text")] + public string Text { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusCreator.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusCreator.cs new file mode 100644 index 00000000..e6675774 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusCreator.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.FanFicus.Types; + +internal class FanFicusCreator +{ + [JsonPropertyName("_id")] + public string Id { get; set; } + + [JsonPropertyName("nickName")] + public string NickName { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusImage.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusImage.cs new file mode 100644 index 00000000..ba788b1b --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusImage.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.FanFicus.Types; + +internal class FanFicusImage +{ + [JsonPropertyName("url")] + public string Url { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusPart.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusPart.cs new file mode 100644 index 00000000..dba47c99 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusPart.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.FanFicus.Types; + +internal class FanFicusPart +{ + [JsonPropertyName("_id")] + public string Id { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusUser.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusUser.cs new file mode 100644 index 00000000..c94eb60d --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.FanFicus/Types/FanFicusUser.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.FanFicus.Types; + +internal class FanFicusUser +{ + [JsonPropertyName("token")] + public string Token { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Fb2Top/Elib2Ebook.ExternalServices.Fb2Top.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Fb2Top/Elib2Ebook.ExternalServices.Fb2Top.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Fb2Top/Elib2Ebook.ExternalServices.Fb2Top.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/Fb2Top/Fb2TopGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Fb2Top/Getters/Fb2TopGetter.cs similarity index 61% rename from src/Core/Logic/Getters/Fb2Top/Fb2TopGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Fb2Top/Getters/Fb2TopGetter.cs index 029330da..8eda498e 100644 --- a/src/Core/Logic/Getters/Fb2Top/Fb2TopGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Fb2Top/Getters/Fb2TopGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.Fb2Top; +namespace Elib2Ebook.ExternalServices.Fb2Top.Getters; public class Fb2TopGetter(BookGetterConfig config) : Fb2TopGetterBase(config) { diff --git a/src/Core/Logic/Getters/Fb2Top/Fb2TopGetterBase.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Fb2Top/Getters/Fb2TopGetterBase.cs similarity index 93% rename from src/Core/Logic/Getters/Fb2Top/Fb2TopGetterBase.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Fb2Top/Getters/Fb2TopGetterBase.cs index 4a567ec1..751e82ba 100644 --- a/src/Core/Logic/Getters/Fb2Top/Fb2TopGetterBase.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Fb2Top/Getters/Fb2TopGetterBase.cs @@ -1,12 +1,13 @@ -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.Fb2Top; +namespace Elib2Ebook.ExternalServices.Fb2Top.Getters; public abstract class Fb2TopGetterBase(BookGetterConfig config) : GetterBase(config) { diff --git a/src/Core/Logic/Getters/Fb2Top/LadyLibGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Fb2Top/Getters/LadyLibGetter.cs similarity index 61% rename from src/Core/Logic/Getters/Fb2Top/LadyLibGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Fb2Top/Getters/LadyLibGetter.cs index 6e2a776c..5adc9cf6 100644 --- a/src/Core/Logic/Getters/Fb2Top/LadyLibGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Fb2Top/Getters/LadyLibGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.Fb2Top; +namespace Elib2Ebook.ExternalServices.Fb2Top.Getters; public class LadyLibGetter(BookGetterConfig config) : Fb2TopGetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Ficbook/Elib2Ebook.ExternalServices.Ficbook.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Ficbook/Elib2Ebook.ExternalServices.Ficbook.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Ficbook/Elib2Ebook.ExternalServices.Ficbook.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/FicbookGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Ficbook/FicbookGetter.cs similarity index 94% rename from src/Core/Logic/Getters/FicbookGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Ficbook/FicbookGetter.cs index cdb6408d..fa337423 100644 --- a/src/Core/Logic/Getters/FicbookGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Ficbook/FicbookGetter.cs @@ -1,13 +1,14 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Ficbook; public class FicbookGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.FictionBook/Elib2Ebook.ExternalServices.FictionBook.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.FictionBook/Elib2Ebook.ExternalServices.FictionBook.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.FictionBook/Elib2Ebook.ExternalServices.FictionBook.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/FictionBookGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.FictionBook/FictionBookGetter.cs similarity index 95% rename from src/Core/Logic/Getters/FictionBookGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.FictionBook/FictionBookGetter.cs index 9c201be5..d87e805d 100644 --- a/src/Core/Logic/Getters/FictionBookGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.FictionBook/FictionBookGetter.cs @@ -1,13 +1,14 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.FictionBook; public class FictionBookGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Elib2Ebook.ExternalServices.Freedlit.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Elib2Ebook.ExternalServices.Freedlit.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Elib2Ebook.ExternalServices.Freedlit.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/FreedlitGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/FreedlitGetter.cs similarity index 94% rename from src/Core/Logic/Getters/FreedlitGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/FreedlitGetter.cs index 047adf5f..af63a4db 100644 --- a/src/Core/Logic/Getters/FreedlitGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/FreedlitGetter.cs @@ -1,16 +1,17 @@ using System.Net.Http.Json; using System.Text.Json.Nodes; using System.Web; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.Freedlit; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.Freedlit.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Freedlit; public class FreedlitGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitApiResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitApiResponse.cs new file mode 100644 index 00000000..9fecd96e --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitApiResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Freedlit.Types; + +internal class FreedlitApiResponse +{ + [JsonPropertyName("success")] + public T Success { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitApp.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitApp.cs new file mode 100644 index 00000000..6e209e24 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitApp.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Freedlit.Types; + +internal class FreedlitApp +{ + [JsonPropertyName("props")] + public FreedlitProps Props { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitAuthError.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitAuthError.cs new file mode 100644 index 00000000..2a05797c --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitAuthError.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Freedlit.Types; + +internal class FreedlitAuthError +{ + [JsonPropertyName("email")] + public string Email { get; set; } + + [JsonPropertyName("password")] + public string[] Password { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitAuthor.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitAuthor.cs new file mode 100644 index 00000000..ae2f69da --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitAuthor.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Freedlit.Types; + +internal class FreedlitAuthor +{ + [JsonPropertyName("id")] + public long Id { get; set; } + + [JsonPropertyName("user_link")] + public string UserLink { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitBook.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitBook.cs new file mode 100644 index 00000000..eb86085d --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitBook.cs @@ -0,0 +1,24 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Freedlit.Types; + +internal class FreedlitBook +{ + [JsonPropertyName("id")] + public long Id { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("cover")] + public string Cover { get; set; } + + [JsonPropertyName("annotation")] + public string Annotation { get; set; } + + [JsonPropertyName("language")] + public string Language { get; set; } + + [JsonPropertyName("main_author")] + public FreedlitAuthor MainAuthor { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitChapter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitChapter.cs new file mode 100644 index 00000000..f2219c06 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitChapter.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Freedlit.Types; + +internal class FreedlitChapter +{ + [JsonPropertyName("id")] + public long Id { get; set; } + + [JsonPropertyName("header")] + public string Header { get; set; } + + [JsonPropertyName("content")] + public string Content { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitItemsContent.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitItemsContent.cs new file mode 100644 index 00000000..f8ff928e --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitItemsContent.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Freedlit.Types; + +internal class FreedlitItemsContent +{ + [JsonPropertyName("items")] + public List Items { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitProps.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitProps.cs new file mode 100644 index 00000000..2002c555 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitProps.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Freedlit.Types; + +internal class FreedlitProps +{ + [JsonPropertyName("book")] + public FreedlitBook Book { get; set; } + + [JsonPropertyName("errors")] + public TErr Errors { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitSuccessResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitSuccessResponse.cs new file mode 100644 index 00000000..2710f1a6 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedlit/Types/FreedlitSuccessResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Freedlit.Types; + +internal class FreedlitSuccessResponse +{ + [JsonPropertyName("items")] + public List Items { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Freedom/Elib2Ebook.ExternalServices.Freedom.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedom/Elib2Ebook.ExternalServices.Freedom.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedom/Elib2Ebook.ExternalServices.Freedom.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/Freedom/BookHamsterGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedom/Getters/BookHamsterGetter.cs similarity index 62% rename from src/Core/Logic/Getters/Freedom/BookHamsterGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Freedom/Getters/BookHamsterGetter.cs index 7eaebc3b..6cacb9a3 100644 --- a/src/Core/Logic/Getters/Freedom/BookHamsterGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedom/Getters/BookHamsterGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.Freedom; +namespace Elib2Ebook.ExternalServices.Freedom.Getters; public class BookHamsterGetter(BookGetterConfig config) : FreedomGetterBase(config) { diff --git a/src/Core/Logic/Getters/Freedom/FreedomGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedom/Getters/FreedomGetter.cs similarity index 61% rename from src/Core/Logic/Getters/Freedom/FreedomGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Freedom/Getters/FreedomGetter.cs index 5fd30127..18d6060c 100644 --- a/src/Core/Logic/Getters/Freedom/FreedomGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedom/Getters/FreedomGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.Freedom; +namespace Elib2Ebook.ExternalServices.Freedom.Getters; public class FreedomGetter(BookGetterConfig config) : FreedomGetterBase(config) { diff --git a/src/Core/Logic/Getters/Freedom/FreedomGetterBase.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedom/Getters/FreedomGetterBase.cs similarity index 92% rename from src/Core/Logic/Getters/Freedom/FreedomGetterBase.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Freedom/Getters/FreedomGetterBase.cs index 67a59bcc..217dc448 100644 --- a/src/Core/Logic/Getters/Freedom/FreedomGetterBase.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Freedom/Getters/FreedomGetterBase.cs @@ -1,12 +1,13 @@ -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.Freedom; +namespace Elib2Ebook.ExternalServices.Freedom.Getters; public abstract class FreedomGetterBase(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.HogwartsNet/Elib2Ebook.ExternalServices.HogwartsNet.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.HogwartsNet/Elib2Ebook.ExternalServices.HogwartsNet.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.HogwartsNet/Elib2Ebook.ExternalServices.HogwartsNet.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/HogwartsNetGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.HogwartsNet/HogwartsNetGetter.cs similarity index 95% rename from src/Core/Logic/Getters/HogwartsNetGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.HogwartsNet/HogwartsNetGetter.cs index 965fdfbc..ae2fdbe7 100644 --- a/src/Core/Logic/Getters/HogwartsNetGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.HogwartsNet/HogwartsNetGetter.cs @@ -1,13 +1,14 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.HogwartsNet; public class HogwartsNetGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Elib2Ebook.ExternalServices.HotNovelPub.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Elib2Ebook.ExternalServices.HotNovelPub.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Elib2Ebook.ExternalServices.HotNovelPub.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/HotNovelPub/EzNovelsGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters/EzNovelsGetter.cs similarity index 67% rename from src/Core/Logic/Getters/HotNovelPub/EzNovelsGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters/EzNovelsGetter.cs index 9f81dcc2..39bca9ac 100644 --- a/src/Core/Logic/Getters/HotNovelPub/EzNovelsGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters/EzNovelsGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.HotNovelPub; +namespace Elib2Ebook.ExternalServices.HotNovelPub.Getters; public class EzNovelsGetter(BookGetterConfig config) : HotNovelPubGetterBase(config) { diff --git a/src/Core/Logic/Getters/HotNovelPub/HotNovelPubGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters/HotNovelPubGetter.cs similarity index 67% rename from src/Core/Logic/Getters/HotNovelPub/HotNovelPubGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters/HotNovelPubGetter.cs index b76bda25..4a4ec45e 100644 --- a/src/Core/Logic/Getters/HotNovelPub/HotNovelPubGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters/HotNovelPubGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.HotNovelPub; +namespace Elib2Ebook.ExternalServices.HotNovelPub.Getters; public class HotNovelPubGetter(BookGetterConfig config) : HotNovelPubGetterBase(config) { diff --git a/src/Core/Logic/Getters/HotNovelPub/HotNovelPubGetterBase.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters/HotNovelPubGetterBase.cs similarity index 92% rename from src/Core/Logic/Getters/HotNovelPub/HotNovelPubGetterBase.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters/HotNovelPubGetterBase.cs index 36899ae3..156263d8 100644 --- a/src/Core/Logic/Getters/HotNovelPub/HotNovelPubGetterBase.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters/HotNovelPubGetterBase.cs @@ -1,15 +1,16 @@ using System.Net.Http.Json; using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.HotNovelPub; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.HotNovelPub.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.HotNovelPub; +namespace Elib2Ebook.ExternalServices.HotNovelPub.Getters; public abstract class HotNovelPubGetterBase(BookGetterConfig config) : GetterBase(config) { diff --git a/src/Core/Logic/Getters/HotNovelPub/LaNovelsGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters/LaNovelsGetter.cs similarity index 67% rename from src/Core/Logic/Getters/HotNovelPub/LaNovelsGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters/LaNovelsGetter.cs index 0a175590..9128b65e 100644 --- a/src/Core/Logic/Getters/HotNovelPub/LaNovelsGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters/LaNovelsGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.HotNovelPub; +namespace Elib2Ebook.ExternalServices.HotNovelPub.Getters; public class LaNovelsGetter(BookGetterConfig config) : HotNovelPubGetterBase(config) { diff --git a/src/Core/Logic/Getters/HotNovelPub/LightNovelDailyGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters/LightNovelDailyGetter.cs similarity index 68% rename from src/Core/Logic/Getters/HotNovelPub/LightNovelDailyGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters/LightNovelDailyGetter.cs index c4c62734..d84d2d01 100644 --- a/src/Core/Logic/Getters/HotNovelPub/LightNovelDailyGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Getters/LightNovelDailyGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.HotNovelPub; +namespace Elib2Ebook.ExternalServices.HotNovelPub.Getters; public class LightNovelDailyGetter(BookGetterConfig config) : HotNovelPubGetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubApiResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubApiResponse.cs new file mode 100644 index 00000000..c19607a3 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubApiResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.HotNovelPub.Types; + +internal class HotNovelPubApiResponse +{ + [JsonPropertyName("data")] + public T Data { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubAuthorize.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubAuthorize.cs new file mode 100644 index 00000000..1e573d35 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubAuthorize.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.HotNovelPub.Types; + +internal class HotNovelPubAuthorize +{ + [JsonPropertyName("id")] + public int Id { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("slug")] + public string Slug { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubBook.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubBook.cs new file mode 100644 index 00000000..8c904629 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubBook.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.HotNovelPub.Types; + +internal class HotNovelPubBook +{ + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("description")] + public string Description { get; set; } + + [JsonPropertyName("authorize")] + public HotNovelPubAuthorize Authorize { get; set; } + + [JsonPropertyName("image")] + public string Image { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubBookResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubBookResponse.cs new file mode 100644 index 00000000..73e06964 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubBookResponse.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.HotNovelPub.Types; + +internal class HotNovelPubBookResponse +{ + [JsonPropertyName("book")] + public HotNovelPubBook Book { get; set; } + + [JsonPropertyName("chapters")] + public List Chapters { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubChapter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubChapter.cs new file mode 100644 index 00000000..984115c7 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.HotNovelPub/Types/HotNovelPubChapter.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.HotNovelPub.Types; + +internal class HotNovelPubChapter +{ + [JsonPropertyName("id")] + public int Id { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("slug")] + public string Slug { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.HubBook/Elib2Ebook.ExternalServices.HubBook.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.HubBook/Elib2Ebook.ExternalServices.HubBook.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.HubBook/Elib2Ebook.ExternalServices.HubBook.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/HubBookGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.HubBook/HubBookGetter.cs similarity index 91% rename from src/Core/Logic/Getters/HubBookGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.HubBook/HubBookGetter.cs index 23e5fabc..28a47eea 100644 --- a/src/Core/Logic/Getters/HubBookGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.HubBook/HubBookGetter.cs @@ -1,13 +1,14 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.HubBook; public class HubBookGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.IGram/Elib2Ebook.ExternalServices.IGram.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.IGram/Elib2Ebook.ExternalServices.IGram.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.IGram/Elib2Ebook.ExternalServices.IGram.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/IGramGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.IGram/IGramGetter.cs similarity index 94% rename from src/Core/Logic/Getters/IGramGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.IGram/IGramGetter.cs index 561a3594..d11a18ee 100644 --- a/src/Core/Logic/Getters/IGramGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.IGram/IGramGetter.cs @@ -1,15 +1,16 @@ using System.Net.Http.Json; using System.Text.RegularExpressions; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.Litexit; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.IGram.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.IGram; public class IGramGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.IGram/Types/LitexitChapter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.IGram/Types/LitexitChapter.cs new file mode 100644 index 00000000..6119a3ea --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.IGram/Types/LitexitChapter.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.IGram.Types; + +internal class LitexitChapter +{ + [JsonPropertyName("id")] + public int Id { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("text")] + public string Text { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.IGram/Types/LitexitUser.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.IGram/Types/LitexitUser.cs new file mode 100644 index 00000000..4a231070 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.IGram/Types/LitexitUser.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.IGram.Types; + +internal class LitexitUser +{ + [JsonPropertyName("id")] + public int Id { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Jaomix/Elib2Ebook.ExternalServices.Jaomix.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Jaomix/Elib2Ebook.ExternalServices.Jaomix.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Jaomix/Elib2Ebook.ExternalServices.Jaomix.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/JaomixGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Jaomix/JaomixGetter.cs similarity index 93% rename from src/Core/Logic/Getters/JaomixGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Jaomix/JaomixGetter.cs index dbfc0988..eccb9cc6 100644 --- a/src/Core/Logic/Getters/JaomixGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Jaomix/JaomixGetter.cs @@ -1,13 +1,14 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Jaomix; public class JaomixGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Elib2Ebook.ExternalServices.LibSocial.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Elib2Ebook.ExternalServices.LibSocial.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Elib2Ebook.ExternalServices.LibSocial.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/LibSocial/HentaiLibGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/HentaiLibGetter.cs similarity index 73% rename from src/Core/Logic/Getters/LibSocial/HentaiLibGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/HentaiLibGetter.cs index 15ce779c..47ec8164 100644 --- a/src/Core/Logic/Getters/LibSocial/HentaiLibGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/HentaiLibGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.LibSocial; +namespace Elib2Ebook.ExternalServices.LibSocial.Getters; public class HentaiLibGetter(BookGetterConfig config) : MangaLibGetterBase(config) { diff --git a/src/Core/Logic/Getters/LibSocial/MangaLibGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/MangaLibGetter.cs similarity index 73% rename from src/Core/Logic/Getters/LibSocial/MangaLibGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/MangaLibGetter.cs index 0e7d47c2..cd0ba323 100644 --- a/src/Core/Logic/Getters/LibSocial/MangaLibGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/MangaLibGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.LibSocial; +namespace Elib2Ebook.ExternalServices.LibSocial.Getters; public class MangaLibGetter(BookGetterConfig config) : MangaLibGetterBase(config) { diff --git a/src/Core/Logic/Getters/LibSocial/MangaLibGetterBase.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/MangaLibGetterBase.cs similarity index 71% rename from src/Core/Logic/Getters/LibSocial/MangaLibGetterBase.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/MangaLibGetterBase.cs index 514375a0..32f58ed0 100644 --- a/src/Core/Logic/Getters/LibSocial/MangaLibGetterBase.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/MangaLibGetterBase.cs @@ -1,10 +1,10 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.SocialLib; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.ExternalServices.LibSocial.Types.SocialLib; using HtmlAgilityPack; -namespace Core.Logic.Getters.LibSocial; +namespace Elib2Ebook.ExternalServices.LibSocial.Getters; public abstract class MangaLibGetterBase(BookGetterConfig config) : NewLibSocialGetterBase(config) { diff --git a/src/Core/Logic/Getters/LibSocial/MangaLibOrgGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/MangaLibOrgGetter.cs similarity index 74% rename from src/Core/Logic/Getters/LibSocial/MangaLibOrgGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/MangaLibOrgGetter.cs index 4d548c5a..98630922 100644 --- a/src/Core/Logic/Getters/LibSocial/MangaLibOrgGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/MangaLibOrgGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.LibSocial; +namespace Elib2Ebook.ExternalServices.LibSocial.Getters; public class MangaLibOrgGetter(BookGetterConfig config) : MangaLibGetterBase(config) { diff --git a/src/Core/Logic/Getters/LibSocial/NewLibSocialGetterBase.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/NewLibSocialGetterBase.cs similarity index 97% rename from src/Core/Logic/Getters/LibSocial/NewLibSocialGetterBase.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/NewLibSocialGetterBase.cs index 45b2963b..3406d7ef 100644 --- a/src/Core/Logic/Getters/LibSocial/NewLibSocialGetterBase.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/NewLibSocialGetterBase.cs @@ -3,16 +3,17 @@ using System.Net.Http.Json; using System.Security.Cryptography; using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.SocialLib; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.LibSocial.Types.SocialLib; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.LibSocial; +namespace Elib2Ebook.ExternalServices.LibSocial.Getters; public abstract class NewLibSocialGetterBase(BookGetterConfig config) : GetterBase(config) { diff --git a/src/Core/Logic/Getters/LibSocial/RanobeLibGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/RanobeLibGetter.cs similarity index 95% rename from src/Core/Logic/Getters/LibSocial/RanobeLibGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/RanobeLibGetter.cs index 6fa45a10..077ef4dd 100644 --- a/src/Core/Logic/Getters/LibSocial/RanobeLibGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/RanobeLibGetter.cs @@ -1,13 +1,13 @@ using System.Text; using System.Text.Json; using System.Text.Json.Nodes; -using Core.Configs; -using Core.Extensions; -using Core.Types.SocialLib; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.ExternalServices.LibSocial.Types.SocialLib; using HtmlAgilityPack; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.LibSocial; +namespace Elib2Ebook.ExternalServices.LibSocial.Getters; public class RanobeLibGetter(BookGetterConfig config) : NewLibSocialGetterBase(config) { diff --git a/src/Core/Logic/Getters/LibSocial/SlashLibGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/SlashLibGetter.cs similarity index 70% rename from src/Core/Logic/Getters/LibSocial/SlashLibGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/SlashLibGetter.cs index 8a076762..0022e435 100644 --- a/src/Core/Logic/Getters/LibSocial/SlashLibGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/SlashLibGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.LibSocial; +namespace Elib2Ebook.ExternalServices.LibSocial.Getters; public class SlashLibGetter : MangaLibGetter { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/MangaLib/MangaLibPg.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/MangaLib/MangaLibPg.cs new file mode 100644 index 00000000..909654f9 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/MangaLib/MangaLibPg.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.LibSocial.Types.MangaLib; + +internal class MangaLibPg +{ + [JsonPropertyName("p")] + public int P { get; set; } + + [JsonPropertyName("u")] + public string U { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/LibSocialToken.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/LibSocialToken.cs new file mode 100644 index 00000000..b8a84dc7 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/LibSocialToken.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.LibSocial.Types.SocialLib; + +internal class LibSocialToken +{ + [JsonPropertyName("access_token")] + public string AccessToken { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/RanobeLibBookDetail.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/RanobeLibBookDetail.cs new file mode 100644 index 00000000..431364d6 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/RanobeLibBookDetail.cs @@ -0,0 +1,53 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.LibSocial.Types.SocialLib; + +internal class RanobeLibBookDetails +{ + [JsonPropertyName("data")] + public RLBDData Data { get; set; } +} + +internal class RLBDData +{ + public RLBDData(int? id) + { + Id = id; + } + + [JsonPropertyName("id")] + public int? Id { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("rus_name")] + public string RusName { get; set; } + + [JsonPropertyName("slug_url")] + public string SlugUrl { get; set; } + + [JsonPropertyName("cover")] + public RLBDCover Cover { get; set; } + + [JsonPropertyName("authors")] + public List Authors { get; set; } + + [JsonPropertyName("summary")] + public string Summary { get; set; } +} + +internal class RLBDCover +{ + [JsonPropertyName("default")] + public string Default { get; set; } +} + +internal class RLBDAuthor +{ + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("slug_url")] + public string SlugUrl { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibBookChapters.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibBookChapters.cs new file mode 100644 index 00000000..baffe090 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibBookChapters.cs @@ -0,0 +1,100 @@ +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.LibSocial.Types.SocialLib; + +internal class SocialLibBookChapters +{ + [JsonPropertyName("data")] + public List Chapters { get; set; } +} + +internal class SocialLibBookChapterResponse +{ + [JsonPropertyName("data")] + public SocialLibBookChapter Data { get; set; } +} + +public class SocialLibChapterBranch +{ + [JsonPropertyName("branch_id")] + public long? BranchId { get; set; } +} + +public class SocialLibChapterAttachment +{ + [JsonPropertyName("url")] + public string Url { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } +} + +public class SocialLibBookChapter +{ + [JsonPropertyName("volume")] + public string Volume { get; set; } + + [JsonPropertyName("number")] + public string Number { get; set; } + + [JsonPropertyName("branches")] + public List Branches { get; set; } + + [JsonPropertyName("attachments")] + public SocialLibChapterAttachment[] Attachments { get; set; } = []; + + [JsonPropertyName("pages")] + public SocialLibPage[] Pages { get; set; } = []; + + [JsonPropertyName("content")] + public JsonNode Content { get; set; } + + private string RawName { get; set; } + + [JsonPropertyName("name")] + public string Name + { + get + { + var name = $"Том {Volume} Глава {Number}"; + if (!string.IsNullOrWhiteSpace(RawName)) + { + name += $" - {RawName}"; + } + + return name; + } + set { RawName = value; } + } +} + +internal class SocialLibChapterContent +{ + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("text")] + public string Text { get; set; } + + [JsonPropertyName("marks")] + public SocialLibChapterMark[] Marks { get; set; } = []; + + [JsonPropertyName("content")] + public SocialLibChapterContent[] Content { get; set; } = []; + + [JsonPropertyName("attrs")] + public Dictionary Attrs { get; set; } = new(); +} + +internal class SocialLibChapterMark +{ + [JsonPropertyName("type")] + public string Type { get; set; } +} + +public class SocialLibPage +{ + [JsonPropertyName("url")] + public string Url { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibChapter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibChapter.cs new file mode 100644 index 00000000..7741a404 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibChapter.cs @@ -0,0 +1,23 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.LibSocial.Types.SocialLib; + +internal class SocialLibChapter +{ + [JsonPropertyName("chapter_number")] + public string ChapterNumber { get; set; } + + [JsonPropertyName("chapter_volume")] + public int ChapterVolume { get; set; } + + [JsonPropertyName("chapter_name")] + public string ChapterName { get; set; } + + [JsonPropertyName("chapter_slug")] + public string ChapterSlug { get; set; } + + [JsonPropertyName("branch_id")] + public int? BranchId { get; set; } + + public string Name => $"Том {ChapterVolume} Глава {ChapterNumber} {ChapterName}".Trim(); +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibChapterOld.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibChapterOld.cs new file mode 100644 index 00000000..4b76f226 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibChapterOld.cs @@ -0,0 +1,23 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.LibSocial.Types.SocialLib; + +internal class SocialLibChapterOld +{ + [JsonPropertyName("number")] + public string ChapterNumber { get; set; } + + [JsonPropertyName("volume")] + public string ChapterVolume { get; set; } + + [JsonPropertyName("name")] + public string ChapterName { get; set; } + + [JsonPropertyName("slug")] + public string ChapterSlug { get; set; } + + public string GetName() + { + return $"Том {ChapterVolume} Глава {ChapterNumber} {ChapterName}".Trim(); + } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibChapters.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibChapters.cs new file mode 100644 index 00000000..6a4e5a5a --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibChapters.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.LibSocial.Types.SocialLib; + +internal class SocialLibChapters +{ + [JsonPropertyName("list")] + public List List { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/User.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/User.cs new file mode 100644 index 00000000..bc50ef56 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/User.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.LibSocial.Types.SocialLib; + +internal class User +{ + [JsonPropertyName("id")] + public long Id { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/WindowData.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/WindowData.cs new file mode 100644 index 00000000..9514db2b --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/WindowData.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.LibSocial.Types.SocialLib; + +internal class WindowData +{ + [JsonPropertyName("chapters")] + public SocialLibChapters Chapters { get; set; } + + [JsonPropertyName("user")] + public User User { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Libbox/Elib2Ebook.ExternalServices.Libbox.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Libbox/Elib2Ebook.ExternalServices.Libbox.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Libbox/Elib2Ebook.ExternalServices.Libbox.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/LibboxGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Libbox/LibboxGetter.cs similarity index 95% rename from src/Core/Logic/Getters/LibboxGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Libbox/LibboxGetter.cs index 33b2ca23..396340e3 100644 --- a/src/Core/Logic/Getters/LibboxGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Libbox/LibboxGetter.cs @@ -1,13 +1,14 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Libbox; public class LibboxGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Libking/Elib2Ebook.ExternalServices.Libking.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Libking/Elib2Ebook.ExternalServices.Libking.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Libking/Elib2Ebook.ExternalServices.Libking.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/LibkingGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Libking/LibkingGetter.cs similarity index 94% rename from src/Core/Logic/Getters/LibkingGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Libking/LibkingGetter.cs index 373c355a..3520247a 100644 --- a/src/Core/Logic/Getters/LibkingGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Libking/LibkingGetter.cs @@ -1,13 +1,14 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Libking; public class LibkingGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Librebook/Elib2Ebook.ExternalServices.Librebook.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Librebook/Elib2Ebook.ExternalServices.Librebook.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Librebook/Elib2Ebook.ExternalServices.Librebook.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/LibrebookGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Librebook/LibrebookGetter.cs similarity index 94% rename from src/Core/Logic/Getters/LibrebookGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Librebook/LibrebookGetter.cs index de2b648d..aa4425f3 100644 --- a/src/Core/Logic/Getters/LibrebookGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Librebook/LibrebookGetter.cs @@ -1,15 +1,16 @@ using System.Text.Json; using System.Text.RegularExpressions; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.Librebook; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.Librebook.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Librebook; public class LibrebookGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Librebook/Types/LibrebookAuthResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Librebook/Types/LibrebookAuthResponse.cs new file mode 100644 index 00000000..beae6257 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Librebook/Types/LibrebookAuthResponse.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Librebook.Types; + +internal class LibrebookAuthResponse +{ + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("text")] + public string Text { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Libst/Elib2Ebook.ExternalServices.Libst.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Libst/Elib2Ebook.ExternalServices.Libst.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Libst/Elib2Ebook.ExternalServices.Libst.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/LibstGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Libst/LibstGetter.cs similarity index 95% rename from src/Core/Logic/Getters/LibstGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Libst/LibstGetter.cs index a80af18b..5bcb0c5b 100644 --- a/src/Core/Logic/Getters/LibstGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Libst/LibstGetter.cs @@ -1,15 +1,16 @@ using System.Net; using System.Text; using System.Text.RegularExpressions; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Libst; public class LibstGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.LitLife/Elib2Ebook.ExternalServices.LitLife.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.LitLife/Elib2Ebook.ExternalServices.LitLife.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LitLife/Elib2Ebook.ExternalServices.LitLife.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/LitLifeGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LitLife/LitLifeGetter.cs similarity index 95% rename from src/Core/Logic/Getters/LitLifeGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.LitLife/LitLifeGetter.cs index 2d26abf2..03689f59 100644 --- a/src/Core/Logic/Getters/LitLifeGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LitLife/LitLifeGetter.cs @@ -1,13 +1,14 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.LitLife; public class LitLifeGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/Elib2Ebook.ExternalServices.Litgorod.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/Elib2Ebook.ExternalServices.Litgorod.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/Elib2Ebook.ExternalServices.Litgorod.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/LitgorodGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/LitgorodGetter.cs similarity index 95% rename from src/Core/Logic/Getters/LitgorodGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/LitgorodGetter.cs index 56e7711a..9b1249d0 100644 --- a/src/Core/Logic/Getters/LitgorodGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/LitgorodGetter.cs @@ -1,16 +1,17 @@ using System.Net.Http.Json; using System.Text; using System.Web; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.Litgorod; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.Litgorod.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Litgorod; public class LitgorodGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/Types/LitgorodAuthResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/Types/LitgorodAuthResponse.cs new file mode 100644 index 00000000..30cfe3fe --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/Types/LitgorodAuthResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litgorod.Types; + +internal class LitgorodAuthResponse +{ + [JsonPropertyName("message")] + public string Message { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/Types/LitgorodBookPart.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/Types/LitgorodBookPart.cs new file mode 100644 index 00000000..e62b48e6 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/Types/LitgorodBookPart.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litgorod.Types; + +internal class LitgorodBookPart +{ + [JsonPropertyName("text")] + public string Text { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/Types/LitgorodChapter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/Types/LitgorodChapter.cs new file mode 100644 index 00000000..089eda7f --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litgorod/Types/LitgorodChapter.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litgorod.Types; + +internal class LitgorodChapter +{ + [JsonPropertyName("explodedParagraphs")] + public string[] Paragraphs { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Elib2Ebook.ExternalServices.Litmarket.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Elib2Ebook.ExternalServices.Litmarket.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Elib2Ebook.ExternalServices.Litmarket.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/LitmarketGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/LitmarketGetter.cs similarity index 96% rename from src/Core/Logic/Getters/LitmarketGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/LitmarketGetter.cs index c18f707e..a7265683 100644 --- a/src/Core/Logic/Getters/LitmarketGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/LitmarketGetter.cs @@ -1,16 +1,17 @@ using System.Net.Http.Json; using System.Text; using System.Web; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.Litmarket; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.Litmarket.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Litmarket; public class LitmarketGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/AuthResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/AuthResponse.cs new file mode 100644 index 00000000..44a93df2 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/AuthResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litmarket.Types; + +internal class AuthResponse +{ + [JsonPropertyName("success")] + public bool Success { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/Block.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/Block.cs new file mode 100644 index 00000000..ea7d5810 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/Block.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litmarket.Types; + +internal class Block +{ + [JsonPropertyName("chunk")] + public Chunk Chunk { get; set; } + + [JsonPropertyName("index")] + public int Index { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/Chunk.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/Chunk.cs new file mode 100644 index 00000000..3c551fc4 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/Chunk.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litmarket.Types; + +internal class Chunk +{ + [JsonPropertyName("mods")] + public Mod[] Mods { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/LBook.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/LBook.cs new file mode 100644 index 00000000..c6d4cd6a --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/LBook.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litmarket.Types; + +internal class LBook +{ + [JsonPropertyName("ebookId")] + public int EbookId { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/Mod.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/Mod.cs new file mode 100644 index 00000000..7ea8726f --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/Mod.cs @@ -0,0 +1,22 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litmarket.Types; + +internal class Mod +{ + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("styles")] + public string[] Styles { get; set; } + + [JsonPropertyName("text")] + public string Text { get; set; } + + [JsonPropertyName("data")] + public JsonElement Data { get; set; } + + [JsonPropertyName("mods")] + public Mod[] Mods { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/Response.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/Response.cs new file mode 100644 index 00000000..246b386e --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/Types/Response.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litmarket.Types; + +internal class Response +{ + [JsonPropertyName("book")] + public LBook Book { get; set; } + + [JsonPropertyName("tableOfContent")] + public string Toc { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litmir/Elib2Ebook.ExternalServices.Litmir.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmir/Elib2Ebook.ExternalServices.Litmir.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmir/Elib2Ebook.ExternalServices.Litmir.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/LitmirGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmir/LitmirGetter.cs similarity index 96% rename from src/Core/Logic/Getters/LitmirGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Litmir/LitmirGetter.cs index 84fe6308..ac9ab911 100644 --- a/src/Core/Logic/Getters/LitmirGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmir/LitmirGetter.cs @@ -1,13 +1,14 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Litmir; public class LitmirGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Elib2Ebook.ExternalServices.Litnet.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Elib2Ebook.ExternalServices.Litnet.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Elib2Ebook.ExternalServices.Litnet.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/Litnet/BooknetComGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Getters/BooknetComGetter.cs similarity index 62% rename from src/Core/Logic/Getters/Litnet/BooknetComGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Getters/BooknetComGetter.cs index fa40902b..4813fb3f 100644 --- a/src/Core/Logic/Getters/Litnet/BooknetComGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Getters/BooknetComGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.Litnet; +namespace Elib2Ebook.ExternalServices.Litnet.Getters; public class BooknetComGetter(BookGetterConfig config) : LitnetGetterBase(config) { diff --git a/src/Core/Logic/Getters/Litnet/BooknetUaGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Getters/BooknetUaGetter.cs similarity index 62% rename from src/Core/Logic/Getters/Litnet/BooknetUaGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Getters/BooknetUaGetter.cs index ba40fe8e..b0885c7b 100644 --- a/src/Core/Logic/Getters/Litnet/BooknetUaGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Getters/BooknetUaGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.Litnet; +namespace Elib2Ebook.ExternalServices.Litnet.Getters; public class BooknetUaGetter(BookGetterConfig config) : LitnetGetterBase(config) { diff --git a/src/Core/Logic/Getters/Litnet/LitnetGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Getters/LitnetGetter.cs similarity index 61% rename from src/Core/Logic/Getters/Litnet/LitnetGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Getters/LitnetGetter.cs index ed762c54..ef25ba94 100644 --- a/src/Core/Logic/Getters/Litnet/LitnetGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Getters/LitnetGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.Litnet; +namespace Elib2Ebook.ExternalServices.Litnet.Getters; public class LitnetGetter(BookGetterConfig config) : LitnetGetterBase(config) { diff --git a/src/Core/Logic/Getters/Litnet/LitnetGetterBase.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Getters/LitnetGetterBase.cs similarity index 97% rename from src/Core/Logic/Getters/Litnet/LitnetGetterBase.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Getters/LitnetGetterBase.cs index 8a14c472..ec14a93f 100644 --- a/src/Core/Logic/Getters/Litnet/LitnetGetterBase.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Getters/LitnetGetterBase.cs @@ -3,16 +3,17 @@ using System.Security.Cryptography; using System.Text; using System.Web; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.Litnet; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.Litnet.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.Litnet; +namespace Elib2Ebook.ExternalServices.Litnet.Getters; public abstract class LitnetGetterBase(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Types/LitnetAuthResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Types/LitnetAuthResponse.cs new file mode 100644 index 00000000..857f9039 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Types/LitnetAuthResponse.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litnet.Types; + +internal class LitnetAuthResponse +{ + [JsonPropertyName("error")] + public string Error { get; set; } + + [JsonPropertyName("token")] + public string Token { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Types/LitnetBookResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Types/LitnetBookResponse.cs new file mode 100644 index 00000000..77eaf5eb --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Types/LitnetBookResponse.cs @@ -0,0 +1,39 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litnet.Types; + +internal class LitnetBookResponse +{ + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("author_name")] + public string AuthorName { get; set; } + + [JsonPropertyName("cover")] + public string Cover { get; set; } + + [JsonPropertyName("annotation")] + public string Annotation { get; set; } + + [JsonPropertyName("url")] + public string Url { get; set; } + + [JsonPropertyName("author_id")] + public long AuthorId { get; set; } + + [JsonPropertyName("lang")] + public string Lang { get; set; } + + [JsonPropertyName("cycle_priority")] + public int? CyclePriority { get; set; } + + [JsonPropertyName("adult_only")] + public bool AdultOnly { get; set; } + + [JsonPropertyName("co_author_name")] + public string CoAuthorName { get; set; } + + [JsonPropertyName("co_author")] + public long? CoAuthorId { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Types/LitnetChapterResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Types/LitnetChapterResponse.cs new file mode 100644 index 00000000..851706f7 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Types/LitnetChapterResponse.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litnet.Types; + +internal class LitnetChapterResponse +{ + [JsonPropertyName("id")] + public long Id { get; set; } + + [JsonPropertyName("text")] + public string Text { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Types/LitnetContentsResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Types/LitnetContentsResponse.cs new file mode 100644 index 00000000..c18328bf --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litnet/Types/LitnetContentsResponse.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litnet.Types; + +internal class LitnetContentsResponse +{ + [JsonPropertyName("id")] + public long Id { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Elib2Ebook.ExternalServices.Litres.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Elib2Ebook.ExternalServices.Litres.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Elib2Ebook.ExternalServices.Litres.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/LitresGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/LitresGetter.cs similarity index 97% rename from src/Core/Logic/Getters/LitresGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Litres/LitresGetter.cs index 3c735d35..6865894b 100644 --- a/src/Core/Logic/Getters/LitresGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/LitresGetter.cs @@ -4,19 +4,19 @@ using System.Security.Cryptography; using System.Text; using System.Text.Json; -using Core.Configs; -using Core.Extensions; -using Core.Misc; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.Litres; -using Core.Types.Litres.Requests; -using Core.Types.Litres.Response; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.Litres.Types; +using Elib2Ebook.ExternalServices.Litres.Types.Requests; +using Elib2Ebook.ExternalServices.Litres.Types.Response; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Litres; public class LitresGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/Core/Types/Litres/LitresBook.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/LitresBook.cs similarity index 61% rename from src/Core/Types/Litres/LitresBook.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/LitresBook.cs index 361a9f44..8773b762 100644 --- a/src/Core/Types/Litres/LitresBook.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/LitresBook.cs @@ -1,8 +1,8 @@ using HtmlAgilityPack; -namespace Core.Types.Litres; +namespace Elib2Ebook.ExternalServices.Litres.Types; -public class LitresBook +internal class LitresBook { public HtmlDocument Content; diff --git a/src/Core/Types/Litres/LitresPayload.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/LitresPayload.cs similarity index 58% rename from src/Core/Types/Litres/LitresPayload.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/LitresPayload.cs index e2003aed..f3c8c748 100644 --- a/src/Core/Types/Litres/LitresPayload.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/LitresPayload.cs @@ -2,21 +2,27 @@ using System.Text; using System.Text.Json.Serialization; -namespace Core.Types.Litres; +namespace Elib2Ebook.ExternalServices.Litres.Types; -public class LitresPayload +internal class LitresPayload { - [JsonPropertyName("sha")] public string Sha { get; set; } + [JsonPropertyName("sha")] + public string Sha { get; set; } - [JsonPropertyName("sid")] public string Sid { get; set; } + [JsonPropertyName("sid")] + public string Sid { get; set; } - [JsonPropertyName("uilang")] public string UiLang { get; set; } = "rus"; + [JsonPropertyName("uilang")] + public string UiLang { get; set; } = "rus"; - [JsonPropertyName("time")] public string Time { get; set; } + [JsonPropertyName("time")] + public string Time { get; set; } - [JsonPropertyName("mobile_app")] public string App { get; set; } + [JsonPropertyName("mobile_app")] + public string App { get; set; } - [JsonPropertyName("requests")] public List Requests { get; set; } = new(); + [JsonPropertyName("requests")] + public List Requests { get; set; } = new(); public static LitresPayload Create(DateTime date, string sid, string secret, string app) { diff --git a/src/Core/Types/Litres/LitresTarget.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/LitresTarget.cs similarity index 59% rename from src/Core/Types/Litres/LitresTarget.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/LitresTarget.cs index 1bae77ad..0590ad01 100644 --- a/src/Core/Types/Litres/LitresTarget.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/LitresTarget.cs @@ -1,6 +1,6 @@ -namespace Core.Types.Litres; +namespace Elib2Ebook.ExternalServices.Litres.Types; -public class LitresTarget +internal class LitresTarget { public string Id { get; set; } public string Target { get; set; } diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresAuthData.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresAuthData.cs new file mode 100644 index 00000000..d192f1af --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresAuthData.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litres.Types.Requests; + +internal class LitresAuthData +{ + [JsonPropertyName("login")] + public string Login { get; set; } + + [JsonPropertyName("pwd")] + public string Password { get; set; } +} diff --git a/src/Core/Types/Litres/Requests/LitresAuthRequest.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresAuthRequest.cs similarity index 56% rename from src/Core/Types/Litres/Requests/LitresAuthRequest.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresAuthRequest.cs index 2011fe80..51712a1a 100644 --- a/src/Core/Types/Litres/Requests/LitresAuthRequest.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresAuthRequest.cs @@ -1,6 +1,6 @@ -namespace Core.Types.Litres.Requests; +namespace Elib2Ebook.ExternalServices.Litres.Types.Requests; -public class LitresAuthRequest : LitresRequestBase +internal class LitresAuthRequest : LitresRequestBase { public LitresAuthRequest(string login, string password) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresBrowseArtsData.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresBrowseArtsData.cs new file mode 100644 index 00000000..8fa155e5 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresBrowseArtsData.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litres.Types.Requests; + +internal class LitresBrowseArtsData +{ + [JsonPropertyName("currency")] + public string Currency { get; set; } + + [JsonPropertyName("anno")] + public string Anno { get; set; } + + [JsonPropertyName("id")] + public string[] Id { get; set; } +} diff --git a/src/Core/Types/Litres/Requests/LitresBrowseArtsRequest.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresBrowseArtsRequest.cs similarity index 55% rename from src/Core/Types/Litres/Requests/LitresBrowseArtsRequest.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresBrowseArtsRequest.cs index 2827d6ff..e0c42dfe 100644 --- a/src/Core/Types/Litres/Requests/LitresBrowseArtsRequest.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresBrowseArtsRequest.cs @@ -1,6 +1,6 @@ -namespace Core.Types.Litres.Requests; +namespace Elib2Ebook.ExternalServices.Litres.Types.Requests; -public class LitresBrowseArtsRequest : LitresRequestBase +internal class LitresBrowseArtsRequest : LitresRequestBase { public LitresBrowseArtsRequest(string[] id) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresRequestBase.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresRequestBase.cs new file mode 100644 index 00000000..0a618bbc --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresRequestBase.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litres.Types.Requests; + +public abstract class LitresRequestBase where T : new() +{ + [JsonPropertyName("id")] + public string Id { get; set; } = "data"; + + [JsonPropertyName("func")] + public string Func { get; set; } + + [JsonPropertyName("param")] + public T Param { get; } = new(); +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresArt.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresArt.cs new file mode 100644 index 00000000..d33c8415 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresArt.cs @@ -0,0 +1,30 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litres.Types.Response; + +internal class LitresArt +{ + [JsonPropertyName("id")] + public long Id { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("html_annotation")] + public string Annotation { get; set; } + + [JsonPropertyName("persons")] + public LitresPerson[] Persons { get; set; } = []; + + [JsonPropertyName("cover_url")] + public string Cover { get; set; } + + [JsonPropertyName("series")] + public LitresSeria[] Sequences { get; set; } = []; + + [JsonPropertyName("linked_arts")] + public LitresArt[] LinkedArts { get; set; } + + [JsonPropertyName("art_type")] + public LitresArtTypeEnum ArtType { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresArtTypeEnum.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresArtTypeEnum.cs new file mode 100644 index 00000000..f55227e2 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresArtTypeEnum.cs @@ -0,0 +1,8 @@ +namespace Elib2Ebook.ExternalServices.Litres.Types.Response; + +internal enum LitresArtTypeEnum +{ + Text = 0, + Audio = 1, + Pdf = 4 +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresAuthResponseData.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresAuthResponseData.cs new file mode 100644 index 00000000..a837e177 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresAuthResponseData.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litres.Types.Response; + +internal class LitresAuthResponseData +{ + [JsonPropertyName("success")] + public bool Success { get; set; } + + [JsonPropertyName("sid")] + public string Sid { get; set; } + + [JsonPropertyName("error_message")] + public string ErrorMessage { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresFile.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresFile.cs new file mode 100644 index 00000000..dc50607e --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresFile.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litres.Types.Response; + +internal class LitresFile +{ + [JsonPropertyName("extension")] + public string Extension { get; set; } + + [JsonPropertyName("id")] + public long Id { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresFiles.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresFiles.cs new file mode 100644 index 00000000..705f2ce1 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresFiles.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litres.Types.Response; + +internal class LitresFiles +{ + [JsonPropertyName("files")] + public LitresFile[] Files { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresMe.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresMe.cs new file mode 100644 index 00000000..7ed2a5bd --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresMe.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litres.Types.Response; + +internal class LitresMe +{ + [JsonPropertyName("partner_subscriptions")] + public LitresPartnerSubscriptions PartnerSubscriptions { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresPartnerSubscriptions.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresPartnerSubscriptions.cs new file mode 100644 index 00000000..9464f290 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresPartnerSubscriptions.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litres.Types.Response; + +internal class LitresPartnerSubscriptions +{ + [JsonPropertyName("subscriptions")] + public LitresSubscription[] Subscriptions { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresPerson.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresPerson.cs new file mode 100644 index 00000000..bee94f35 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresPerson.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litres.Types.Response; + +internal class LitresPerson +{ + [JsonPropertyName("id")] + public TId Id { get; set; } + + [JsonPropertyName("full_name")] + public string FullName { get; set; } + + [JsonPropertyName("role")] + public string Role { get; set; } + + [JsonPropertyName("url")] + public string Url { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresResponse.cs new file mode 100644 index 00000000..4af2506f --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litres.Types.Response; + +internal class LitresResponse +{ + [JsonPropertyName("data")] + public T Data { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresSeria.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresSeria.cs new file mode 100644 index 00000000..def26e3c --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresSeria.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litres.Types.Response; + +internal class LitresSeria +{ + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("id")] + public long Id { get; set; } + + [JsonPropertyName("sequence_number")] + public string SequenceNumber { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresStaticResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresStaticResponse.cs new file mode 100644 index 00000000..7397ccdb --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresStaticResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litres.Types.Response; + +internal class LitresStaticResponse +{ + [JsonPropertyName("payload")] + public LitresResponse Payload { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresSubscription.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresSubscription.cs new file mode 100644 index 00000000..ce057bdf --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresSubscription.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litres.Types.Response; + +internal class LitresSubscription +{ + [JsonPropertyName("is_active")] + public bool IsActive { get; set; } + + [JsonPropertyName("host")] + public string Host { get; set; } + + [JsonPropertyName("type")] + public string Type { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litsovet/Elib2Ebook.ExternalServices.Litsovet.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Litsovet/Elib2Ebook.ExternalServices.Litsovet.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litsovet/Elib2Ebook.ExternalServices.Litsovet.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/LitsovetGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litsovet/LitsovetGetter.cs similarity index 93% rename from src/Core/Logic/Getters/LitsovetGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Litsovet/LitsovetGetter.cs index a40ca485..8fe5a210 100644 --- a/src/Core/Logic/Getters/LitsovetGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litsovet/LitsovetGetter.cs @@ -1,14 +1,15 @@ using System.Net.Http.Json; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.Litsovet; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.Litsovet.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Litsovet; public class LitsovetGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litsovet/Types/LitsovetLoginResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litsovet/Types/LitsovetLoginResponse.cs new file mode 100644 index 00000000..b2892185 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litsovet/Types/LitsovetLoginResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Litsovet.Types; + +internal class LitsovetLoginResponse +{ + [JsonPropertyName("ok")] + public int Ok { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.MangaMammy/Elib2Ebook.ExternalServices.MangaMammy.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.MangaMammy/Elib2Ebook.ExternalServices.MangaMammy.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.MangaMammy/Elib2Ebook.ExternalServices.MangaMammy.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/MangaMammyGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.MangaMammy/MangaMammyGetter.cs similarity index 93% rename from src/Core/Logic/Getters/MangaMammyGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.MangaMammy/MangaMammyGetter.cs index befe5a7e..8588880b 100644 --- a/src/Core/Logic/Getters/MangaMammyGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.MangaMammy/MangaMammyGetter.cs @@ -1,13 +1,14 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.MangaMammy; public class MangaMammyGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.MirKnig/Elib2Ebook.ExternalServices.MirKnig.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.MirKnig/Elib2Ebook.ExternalServices.MirKnig.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.MirKnig/Elib2Ebook.ExternalServices.MirKnig.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/MirKnigGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.MirKnig/MirKnigGetter.cs similarity index 92% rename from src/Core/Logic/Getters/MirKnigGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.MirKnig/MirKnigGetter.cs index 8b9b668d..584db1f6 100644 --- a/src/Core/Logic/Getters/MirKnigGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.MirKnig/MirKnigGetter.cs @@ -1,14 +1,15 @@ using System.Text; using System.Text.RegularExpressions; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.MirKnig; public class MirKnigGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Elib2Ebook.ExternalServices.MyBook.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Elib2Ebook.ExternalServices.MyBook.csproj new file mode 100644 index 00000000..555db2cf --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Elib2Ebook.ExternalServices.MyBook.csproj @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/Core/Logic/Getters/MyBookGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/MyBookGetter.cs similarity index 96% rename from src/Core/Logic/Getters/MyBookGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/MyBookGetter.cs index 19af8887..b4e5dc3f 100644 --- a/src/Core/Logic/Getters/MyBookGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/MyBookGetter.cs @@ -3,18 +3,18 @@ using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Nodes; -using Core.Configs; -using Core.Extensions; -using Core.Misc; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.MyBook; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.MyBook.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; using OAuth; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.MyBook; public class MyBookGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookAuth.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookAuth.cs new file mode 100644 index 00000000..2c15539e --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookAuth.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.MyBook.Types; + +internal class MyBookAuth +{ + [JsonPropertyName("token")] + public string Token { get; set; } + + [JsonPropertyName("secret")] + public string Secret { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookAuthor.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookAuthor.cs new file mode 100644 index 00000000..09f6e1fc --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookAuthor.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.MyBook.Types; + +internal class MyBookAuthor +{ + [JsonPropertyName("cover_name")] + public string Name { get; set; } + + [JsonPropertyName("absolute_url")] + public string Url { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookBook.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookBook.cs new file mode 100644 index 00000000..ca317454 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookBook.cs @@ -0,0 +1,37 @@ +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.MyBook.Types; + +internal class MyBookBook +{ + [JsonPropertyName("id")] + public long Id { get; set; } + + [JsonPropertyName("annotation")] + public string Annotation { get; set; } + + [JsonPropertyName("bookfile")] + public string BookFile { get; set; } + + [JsonPropertyName("default_cover")] + public string Cover { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("authors")] + public MyBookAuthor[] Authors { get; set; } + + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("mapfiles")] + public MyBookMapFile[] MapFiles { get; set; } + + [JsonPropertyName("connected_book")] + public MyBookBook Connected { get; set; } + + [JsonPropertyName("bookfiles")] + public JsonNode[] Files { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookFile.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookFile.cs new file mode 100644 index 00000000..c1ce8e53 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookFile.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.MyBook.Types; + +internal class MyBookFile +{ + [JsonPropertyName("url")] + public string Url { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("order")] + public int Order { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookMapFile.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookMapFile.cs new file mode 100644 index 00000000..a50c561b --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.MyBook/Types/MyBookMapFile.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.MyBook.Types; + +internal class MyBookMapFile +{ + [JsonPropertyName("book")] + public long Book { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Elib2Ebook.ExternalServices.Neobook.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Elib2Ebook.ExternalServices.Neobook.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Elib2Ebook.ExternalServices.Neobook.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/NeobookGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/NeobookGetter.cs similarity index 94% rename from src/Core/Logic/Getters/NeobookGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/NeobookGetter.cs index 94f44407..af1a17f2 100644 --- a/src/Core/Logic/Getters/NeobookGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/NeobookGetter.cs @@ -1,15 +1,16 @@ using System.Net; using System.Net.Http.Json; using System.Text.RegularExpressions; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.Neobook; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.Neobook.Types; using HtmlAgilityPack; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Neobook; public class NeobookGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookAttachment.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookAttachment.cs new file mode 100644 index 00000000..70f86ad5 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookAttachment.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Neobook.Types; + +internal class NeobookAttachment +{ + [JsonPropertyName("image")] + public Dictionary Cover { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookAuth.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookAuth.cs new file mode 100644 index 00000000..4e5efb17 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookAuth.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Neobook.Types; + +internal class NeobookAuth +{ + [JsonPropertyName("error")] + public string Error { get; set; } + + [JsonPropertyName("login")] + public NeobookLogin Login { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookBook.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookBook.cs new file mode 100644 index 00000000..1761315a --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookBook.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Neobook.Types; + +internal class NeobookBook +{ + [JsonPropertyName("chapters")] + public NeobookChapter[] Chapters { get; set; } + + [JsonPropertyName("active_chapter_index")] + public int ActiveChapterIndex { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookChapter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookChapter.cs new file mode 100644 index 00000000..10da2071 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookChapter.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Neobook.Types; + +internal class NeobookChapter +{ + [JsonPropertyName("data")] + public NeobookChapterData Data { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookChapterData.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookChapterData.cs new file mode 100644 index 00000000..a9389221 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookChapterData.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Neobook.Types; + +internal class NeobookChapterData +{ + [JsonPropertyName("html")] + public string Html { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookLogin.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookLogin.cs new file mode 100644 index 00000000..d1ee0601 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookLogin.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Neobook.Types; + +internal class NeobookLogin +{ + [JsonPropertyName("utoken")] + public string Utoken { get; set; } + + [JsonPropertyName("uid")] + public string Uid { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookPostData.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookPostData.cs new file mode 100644 index 00000000..62851635 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookPostData.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Neobook.Types; + +internal class NeobookPostData +{ + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("text")] + public string Text { get; set; } + + [JsonPropertyName("user")] + public NeobookUser User { get; set; } + + [JsonPropertyName("chapters")] + public NeobookTocChapter[] Chapters { get; set; } + + [JsonPropertyName("attachment")] + public NeobookAttachment Attachment { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookTocChapter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookTocChapter.cs new file mode 100644 index 00000000..a4f15413 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookTocChapter.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Neobook.Types; + +internal class NeobookTocChapter +{ + [JsonPropertyName("token")] + public string Token { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookUser.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookUser.cs new file mode 100644 index 00000000..caa40742 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Neobook/Types/NeobookUser.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Neobook.Types; + +internal class NeobookUser +{ + [JsonPropertyName("username")] + public string UserName { get; set; } + + [JsonPropertyName("firstname")] + public string FirstName { get; set; } + + [JsonPropertyName("lastname")] + public string LastName { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.NovelTranslate/Elib2Ebook.ExternalServices.NovelTranslate.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.NovelTranslate/Elib2Ebook.ExternalServices.NovelTranslate.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.NovelTranslate/Elib2Ebook.ExternalServices.NovelTranslate.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/NovelTranslateGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.NovelTranslate/NovelTranslateGetter.cs similarity index 94% rename from src/Core/Logic/Getters/NovelTranslateGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.NovelTranslate/NovelTranslateGetter.cs index bdc0b148..1e244fbf 100644 --- a/src/Core/Logic/Getters/NovelTranslateGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.NovelTranslate/NovelTranslateGetter.cs @@ -1,12 +1,13 @@ -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.NovelTranslate; public class NovelTranslateGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Novelhall/Elib2Ebook.ExternalServices.Novelhall.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Novelhall/Elib2Ebook.ExternalServices.Novelhall.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Novelhall/Elib2Ebook.ExternalServices.Novelhall.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/NovelhallGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Novelhall/NovelhallGetter.cs similarity index 92% rename from src/Core/Logic/Getters/NovelhallGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Novelhall/NovelhallGetter.cs index 5e00a686..fd9fb374 100644 --- a/src/Core/Logic/Getters/NovelhallGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Novelhall/NovelhallGetter.cs @@ -1,12 +1,13 @@ -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Novelhall; public class NovelhallGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Novelxo/Elib2Ebook.ExternalServices.Novelxo.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Novelxo/Elib2Ebook.ExternalServices.Novelxo.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Novelxo/Elib2Ebook.ExternalServices.Novelxo.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/Novelxo/NovelxoGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Novelxo/Getters/NovelxoGetter.cs similarity index 61% rename from src/Core/Logic/Getters/Novelxo/NovelxoGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Novelxo/Getters/NovelxoGetter.cs index 9c89c0e1..3f614464 100644 --- a/src/Core/Logic/Getters/Novelxo/NovelxoGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Novelxo/Getters/NovelxoGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.Novelxo; +namespace Elib2Ebook.ExternalServices.Novelxo.Getters; public class NovelxoGetter(BookGetterConfig config) : NovelxoGetterBase(config) { diff --git a/src/Core/Logic/Getters/Novelxo/NovelxoGetterBase.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Novelxo/Getters/NovelxoGetterBase.cs similarity index 96% rename from src/Core/Logic/Getters/Novelxo/NovelxoGetterBase.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Novelxo/Getters/NovelxoGetterBase.cs index 2287afa9..c49ee9d3 100644 --- a/src/Core/Logic/Getters/Novelxo/NovelxoGetterBase.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Novelxo/Getters/NovelxoGetterBase.cs @@ -1,15 +1,16 @@ using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.Novelxo; +namespace Elib2Ebook.ExternalServices.Novelxo.Getters; public abstract class NovelxoGetterBase(BookGetterConfig config) : GetterBase(config) { diff --git a/src/Core/Logic/Getters/Novelxo/NovelxoRuGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Novelxo/Getters/NovelxoRuGetter.cs similarity index 62% rename from src/Core/Logic/Getters/Novelxo/NovelxoRuGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Novelxo/Getters/NovelxoRuGetter.cs index 6839cd46..f87f3e4b 100644 --- a/src/Core/Logic/Getters/Novelxo/NovelxoRuGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Novelxo/Getters/NovelxoRuGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.Novelxo; +namespace Elib2Ebook.ExternalServices.Novelxo.Getters; public class NovelxoRuGetter(BookGetterConfig config) : NovelxoGetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.OnlineKnigi/Elib2Ebook.ExternalServices.OnlineKnigi.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.OnlineKnigi/Elib2Ebook.ExternalServices.OnlineKnigi.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.OnlineKnigi/Elib2Ebook.ExternalServices.OnlineKnigi.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/OnlineKnigiGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.OnlineKnigi/OnlineKnigiGetter.cs similarity index 91% rename from src/Core/Logic/Getters/OnlineKnigiGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.OnlineKnigi/OnlineKnigiGetter.cs index 27332446..c7849c47 100644 --- a/src/Core/Logic/Getters/OnlineKnigiGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.OnlineKnigi/OnlineKnigiGetter.cs @@ -1,13 +1,14 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.OnlineKnigi; public class OnlineKnigiGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Prodaman/Elib2Ebook.ExternalServices.Prodaman.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Prodaman/Elib2Ebook.ExternalServices.Prodaman.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Prodaman/Elib2Ebook.ExternalServices.Prodaman.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/ProdamanGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Prodaman/ProdamanGetter.cs similarity index 97% rename from src/Core/Logic/Getters/ProdamanGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Prodaman/ProdamanGetter.cs index bf777184..c21ef6ae 100644 --- a/src/Core/Logic/Getters/ProdamanGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Prodaman/ProdamanGetter.cs @@ -1,13 +1,14 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Prodaman; public class ProdamanGetter : GetterBase { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Elib2Ebook.ExternalServices.Ranobe.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Elib2Ebook.ExternalServices.Ranobe.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Elib2Ebook.ExternalServices.Ranobe.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/RanobeGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/RanobeGetter.cs similarity index 90% rename from src/Core/Logic/Getters/RanobeGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/RanobeGetter.cs index 5d07e4b7..ec62a11a 100644 --- a/src/Core/Logic/Getters/RanobeGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/RanobeGetter.cs @@ -1,14 +1,15 @@ using System.Text.Json; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.Ranobe; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.Ranobe.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Ranobe; public class RanobeGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeBook.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeBook.cs new file mode 100644 index 00000000..f5f711ba --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeBook.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Ranobe.Types; + +internal class RanobeBook +{ + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("author")] + public string Author { get; set; } + + [JsonPropertyName("verticalImages")] + public RanobeImage[] Images { get; set; } + + [JsonPropertyName("verticalImage")] + public RanobeImage Image { get; set; } + + [JsonPropertyName("chapters")] + public List Chapters { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeChapter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeChapter.cs new file mode 100644 index 00000000..507863fd --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeChapter.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Ranobe.Types; + +internal class RanobeChapter +{ + [JsonPropertyName("content")] + public RanobeChapterContent Content { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeChapterContent.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeChapterContent.cs new file mode 100644 index 00000000..3a8f6ac6 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeChapterContent.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Ranobe.Types; + +internal class RanobeChapterContent +{ + [JsonPropertyName("text")] + public string Text { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeChapterShort.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeChapterShort.cs new file mode 100644 index 00000000..2a81f023 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeChapterShort.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Ranobe.Types; + +internal class RanobeChapterShort +{ + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("url")] + public string Url { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeImage.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeImage.cs new file mode 100644 index 00000000..fba84cbd --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Ranobe/Types/RanobeImage.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Ranobe.Types; + +internal class RanobeImage +{ + [JsonPropertyName("height")] + public int Height { get; set; } + + [JsonPropertyName("url")] + public string Url { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/Elib2Ebook.ExternalServices.RanobeHub.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/Elib2Ebook.ExternalServices.RanobeHub.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/Elib2Ebook.ExternalServices.RanobeHub.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/RanobeHubGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/RanobeHubGetter.cs similarity index 92% rename from src/Core/Logic/Getters/RanobeHubGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/RanobeHubGetter.cs index 5255c1fa..28e166ae 100644 --- a/src/Core/Logic/Getters/RanobeHubGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/RanobeHubGetter.cs @@ -1,14 +1,15 @@ using System.Net.Http.Json; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.RanobeHub; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.RanobeHub.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.RanobeHub; public class RanobeHubGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/Types/RanobeHubApiResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/Types/RanobeHubApiResponse.cs new file mode 100644 index 00000000..5dd82d8f --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/Types/RanobeHubApiResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.RanobeHub.Types; + +internal class RanobeHubApiResponse +{ + [JsonPropertyName("volumes")] + public RanobeHubVolume[] Volumes { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/Types/RanobeHubChapter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/Types/RanobeHubChapter.cs new file mode 100644 index 00000000..b38d7026 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/Types/RanobeHubChapter.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.RanobeHub.Types; + +internal class RanobeHubChapter +{ + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("url")] + public string Url { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/Types/RanobeHubVolume.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/Types/RanobeHubVolume.cs new file mode 100644 index 00000000..2d6de1e8 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeHub/Types/RanobeHubVolume.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.RanobeHub.Types; + +internal class RanobeHubVolume +{ + [JsonPropertyName("chapters")] + public RanobeHubChapter[] Chapters { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Elib2Ebook.ExternalServices.RanobeOvh.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Elib2Ebook.ExternalServices.RanobeOvh.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Elib2Ebook.ExternalServices.RanobeOvh.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/RanobeOvh/MangaOvhGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Getters/MangaOvhGetter.cs similarity index 77% rename from src/Core/Logic/Getters/RanobeOvh/MangaOvhGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Getters/MangaOvhGetter.cs index 316e49b2..f0ca935b 100644 --- a/src/Core/Logic/Getters/RanobeOvh/MangaOvhGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Getters/MangaOvhGetter.cs @@ -1,11 +1,11 @@ using System.Net.Http.Json; using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.RanobeOvh; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.ExternalServices.RanobeOvh.Types; using HtmlAgilityPack; -namespace Core.Logic.Getters.RanobeOvh; +namespace Elib2Ebook.ExternalServices.RanobeOvh.Getters; public class MangaOvhGetter(BookGetterConfig config) : RanobeOvhGetterBase(config) { diff --git a/src/Core/Logic/Getters/RanobeOvh/NovelOvhGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Getters/NovelOvhGetter.cs similarity index 95% rename from src/Core/Logic/Getters/RanobeOvh/NovelOvhGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Getters/NovelOvhGetter.cs index d12d026b..efd9907d 100644 --- a/src/Core/Logic/Getters/RanobeOvh/NovelOvhGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Getters/NovelOvhGetter.cs @@ -2,13 +2,13 @@ using System.Text; using System.Text.Json; using System.Text.Json.Nodes; -using Core.Configs; -using Core.Extensions; -using Core.Types.RanobeOvh; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.ExternalServices.RanobeOvh.Types; using HtmlAgilityPack; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.RanobeOvh; +namespace Elib2Ebook.ExternalServices.RanobeOvh.Getters; public class NovelOvhGetter(BookGetterConfig config) : RanobeOvhGetterBase(config) { diff --git a/src/Core/Logic/Getters/RanobeOvh/RanobeOvhGetterBase.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Getters/RanobeOvhGetterBase.cs similarity index 92% rename from src/Core/Logic/Getters/RanobeOvh/RanobeOvhGetterBase.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Getters/RanobeOvhGetterBase.cs index 2bc25691..75da435b 100644 --- a/src/Core/Logic/Getters/RanobeOvh/RanobeOvhGetterBase.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Getters/RanobeOvhGetterBase.cs @@ -1,14 +1,15 @@ using System.Text.Json; using System.Text.RegularExpressions; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.RanobeOvh; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.RanobeOvh.Types; using HtmlAgilityPack; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.RanobeOvh; +namespace Elib2Ebook.ExternalServices.RanobeOvh.Getters; public abstract class RanobeOvhGetterBase(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhBranch.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhBranch.cs new file mode 100644 index 00000000..834ffe78 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhBranch.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.RanobeOvh.Types; + +internal class RanobeOvhBranch +{ + [JsonPropertyName("id")] + public string Id { get; set; } + + [JsonPropertyName("translators")] + public RanoveOvhTranslator[] Translators { get; set; } + + [JsonPropertyName("chaptersCount")] + public long ChaptersCount { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhChapterFull.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhChapterFull.cs new file mode 100644 index 00000000..e68a8baa --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhChapterFull.cs @@ -0,0 +1,69 @@ +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.RanobeOvh.Types; + +internal class RanobeOvhChapterFull +{ + [JsonPropertyName("pages")] + public RanobeOvhPage[] Pages { get; set; } + + [JsonPropertyName("content")] + public JsonNode Content { get; set; } +} + +public class RanobeOvhChapterShort +{ + [JsonPropertyName("id")] + public string Id { get; set; } + + [JsonPropertyName("number")] + public decimal Number { get; set; } + + [JsonPropertyName("volume")] + public int? Volume { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("branchId")] + public string BranchId { get; set; } + + public string FullName + { + get + { + if (!Volume.HasValue) + { + return Title; + } + + var shortName = $"Том {Volume}. Глава {(int)Number}"; + return string.IsNullOrWhiteSpace(Title) ? shortName : Title; + } + } +} + +internal class RanobeOvhChapterContent +{ + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("text")] + public string Text { get; set; } + + [JsonPropertyName("marks")] + public RanobeOvhChapterMark[] Marks { get; set; } = []; + + [JsonPropertyName("content")] + public RanobeOvhChapterContent[] Content { get; set; } = []; + + [JsonPropertyName("attrs")] + public Dictionary Attrs { get; set; } = new(); +} + +internal class RanobeOvhChapterMark +{ + [JsonPropertyName("type")] + public string Type { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhManga.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhManga.cs new file mode 100644 index 00000000..03582f23 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhManga.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.RanobeOvh.Types; + +internal class RanobeOvhManga +{ + [JsonPropertyName("name")] + public RanobeOvhMangaName Name { get; set; } + + [JsonPropertyName("poster")] + public string Poster { get; set; } + + [JsonPropertyName("description")] + public string Description { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhMangaName.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhMangaName.cs new file mode 100644 index 00000000..28d1c139 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhMangaName.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.RanobeOvh.Types; + +internal class RanobeOvhMangaName +{ + [JsonPropertyName("ru")] + public string Ru { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhPage.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhPage.cs new file mode 100644 index 00000000..e1b3ddd7 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanobeOvhPage.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.RanobeOvh.Types; + +internal class RanobeOvhPage +{ + [JsonPropertyName("id")] + public string Id { get; set; } + + [JsonPropertyName("image")] + public string Image { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanoveOvhTranslator.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanoveOvhTranslator.cs new file mode 100644 index 00000000..2807c011 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobeOvh/Types/RanoveOvhTranslator.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.RanobeOvh.Types; + +internal class RanoveOvhTranslator +{ + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("slug")] + public string Slug { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.RanobesCom/Elib2Ebook.ExternalServices.RanobesCom.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobesCom/Elib2Ebook.ExternalServices.RanobesCom.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobesCom/Elib2Ebook.ExternalServices.RanobesCom.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/RanobesComGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobesCom/RanobesComGetter.cs similarity index 96% rename from src/Core/Logic/Getters/RanobesComGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.RanobesCom/RanobesComGetter.cs index f533764e..a41bf02b 100644 --- a/src/Core/Logic/Getters/RanobesComGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobesCom/RanobesComGetter.cs @@ -2,16 +2,17 @@ using System.Net.Http.Json; using System.Text; using System.Text.RegularExpressions; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.Ranobes; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.RanobesCom.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.RanobesCom; public class RanobesComGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.RanobesCom/Types/RanobesCookie.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobesCom/Types/RanobesCookie.cs new file mode 100644 index 00000000..51c5f31a --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RanobesCom/Types/RanobesCookie.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.RanobesCom.Types; + +internal class RanobesCookie +{ + [JsonPropertyName("cookie")] + public string Cookie { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.ReadManga/Elib2Ebook.ExternalServices.ReadManga.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.ReadManga/Elib2Ebook.ExternalServices.ReadManga.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.ReadManga/Elib2Ebook.ExternalServices.ReadManga.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/ReadMangaGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.ReadManga/ReadMangaGetter.cs similarity index 93% rename from src/Core/Logic/Getters/ReadMangaGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.ReadManga/ReadMangaGetter.cs index bd1522c4..20f9ac6a 100644 --- a/src/Core/Logic/Getters/ReadMangaGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.ReadManga/ReadMangaGetter.cs @@ -1,15 +1,16 @@ using System.Text; using System.Text.Json; using System.Text.RegularExpressions; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.ReadManga; public class ReadMangaGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Readli/Elib2Ebook.ExternalServices.Readli.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Readli/Elib2Ebook.ExternalServices.Readli.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Readli/Elib2Ebook.ExternalServices.Readli.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/ReadliGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Readli/ReadliGetter.cs similarity index 96% rename from src/Core/Logic/Getters/ReadliGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Readli/ReadliGetter.cs index df3932df..d9ae05d8 100644 --- a/src/Core/Logic/Getters/ReadliGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Readli/ReadliGetter.cs @@ -1,13 +1,14 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Readli; public class ReadliGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Elib2Ebook.ExternalServices.Renovels.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Elib2Ebook.ExternalServices.Renovels.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Elib2Ebook.ExternalServices.Renovels.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/Renovels/RemangaGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Getters/RemangaGetter.cs similarity index 84% rename from src/Core/Logic/Getters/Renovels/RemangaGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Getters/RemangaGetter.cs index 9abb4ec3..36b5cb8a 100644 --- a/src/Core/Logic/Getters/Renovels/RemangaGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Getters/RemangaGetter.cs @@ -1,12 +1,12 @@ using System.Text; using System.Text.Json; using System.Text.Json.Nodes; -using Core.Configs; -using Core.Extensions; -using Core.Types.Renovels; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.ExternalServices.Renovels.Types; using HtmlAgilityPack; -namespace Core.Logic.Getters.Renovels; +namespace Elib2Ebook.ExternalServices.Renovels.Getters; public class RemangaGetter(BookGetterConfig config) : RenovelsGetterBase(config) { diff --git a/src/Core/Logic/Getters/Renovels/RenovelsGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Getters/RenovelsGetter.cs similarity index 65% rename from src/Core/Logic/Getters/Renovels/RenovelsGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Getters/RenovelsGetter.cs index 70b466df..2a1bd0d4 100644 --- a/src/Core/Logic/Getters/Renovels/RenovelsGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Getters/RenovelsGetter.cs @@ -1,9 +1,9 @@ -using Core.Configs; -using Core.Extensions; -using Core.Types.Renovels; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.ExternalServices.Renovels.Types; using HtmlAgilityPack; -namespace Core.Logic.Getters.Renovels; +namespace Elib2Ebook.ExternalServices.Renovels.Getters; public class RenovelsGetter(BookGetterConfig config) : RenovelsGetterBase(config) { diff --git a/src/Core/Logic/Getters/Renovels/RenovelsGetterBase.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Getters/RenovelsGetterBase.cs similarity index 94% rename from src/Core/Logic/Getters/Renovels/RenovelsGetterBase.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Getters/RenovelsGetterBase.cs index 923df0e7..a85f2227 100644 --- a/src/Core/Logic/Getters/Renovels/RenovelsGetterBase.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Getters/RenovelsGetterBase.cs @@ -2,15 +2,16 @@ using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Nodes; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.Renovels; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.Renovels.Types; using HtmlAgilityPack; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.Renovels; +namespace Elib2Ebook.ExternalServices.Renovels.Getters; public abstract class RenovelsGetterBase(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsApiResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsApiResponse.cs new file mode 100644 index 00000000..5992997d --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsApiResponse.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Renovels.Types; + +internal class RenovelsApiResponse +{ + [JsonPropertyName("msg")] + public string Message { get; set; } + + [JsonPropertyName("content")] + public T Content { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsAuthResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsAuthResponse.cs new file mode 100644 index 00000000..d79a7efe --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsAuthResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Renovels.Types; + +internal class RenovelsAuthResponse +{ + [JsonPropertyName("access_token")] + public string Token { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsBranch.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsBranch.cs new file mode 100644 index 00000000..57716326 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsBranch.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Renovels.Types; + +internal class RenovelsBranch +{ + [JsonPropertyName("id")] + public int Id { get; set; } + + [JsonPropertyName("publishers")] + public RenovelsPublisher[] Publishers { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsChapter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsChapter.cs new file mode 100644 index 00000000..be41412d --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsChapter.cs @@ -0,0 +1,45 @@ +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Renovels.Types; + +public class RenovelsChapter +{ + [JsonPropertyName("id")] + public int Id { get; set; } + + [JsonPropertyName("chapter")] + public string Chapter { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("tome")] + public int Tome { get; set; } + + [JsonPropertyName("content")] + public string Content { get; set; } + + [JsonPropertyName("is_paid")] + public bool IsPaid { get; set; } + + [JsonPropertyName("is_bought")] + public bool? IsBought { get; set; } + + [JsonPropertyName("pages")] + public JsonArray Pages { get; set; } + + public string Title + { + get + { + var name = $"Том {Tome}. Глава {Chapter}"; + if (string.IsNullOrWhiteSpace(Name)) + { + return name; + } + + return name + $" - {Name}"; + } + } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsContent.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsContent.cs new file mode 100644 index 00000000..6605cc94 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsContent.cs @@ -0,0 +1,24 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Renovels.Types; + +internal class RenovelsContent +{ + [JsonPropertyName("cover")] + public Dictionary Cover { get; set; } + + [JsonPropertyName("main_name")] + public string MainName { get; set; } + + [JsonPropertyName("secondary_name")] + public string SecondaryName { get; set; } + + [JsonPropertyName("another_name")] + public string AnotherName { get; set; } + + [JsonPropertyName("description")] + public string Description { get; set; } + + [JsonPropertyName("branches")] + public RenovelsBranch[] Branches { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsPage.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsPage.cs new file mode 100644 index 00000000..5fd9e903 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsPage.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Renovels.Types; + +internal class RenovelsPage +{ + [JsonPropertyName("link")] + public string Link { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsPublisher.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsPublisher.cs new file mode 100644 index 00000000..043265c7 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsPublisher.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Renovels.Types; + +internal class RenovelsPublisher +{ + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("dir")] + public string Dir { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsTocResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsTocResponse.cs new file mode 100644 index 00000000..b383aecd --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Renovels/Types/RenovelsTocResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Renovels.Types; + +internal class RenovelsTocResponse +{ + [JsonPropertyName("results")] + public RenovelsChapter[] Results { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Romfant/Elib2Ebook.ExternalServices.Romfant.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Romfant/Elib2Ebook.ExternalServices.Romfant.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Romfant/Elib2Ebook.ExternalServices.Romfant.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/RomfantGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Romfant/RomfantGetter.cs similarity index 94% rename from src/Core/Logic/Getters/RomfantGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Romfant/RomfantGetter.cs index fd41d230..1212ee9e 100644 --- a/src/Core/Logic/Getters/RomfantGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Romfant/RomfantGetter.cs @@ -1,13 +1,14 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Romfant; public class RomfantGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.RoyalRoad/Elib2Ebook.ExternalServices.RoyalRoad.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.RoyalRoad/Elib2Ebook.ExternalServices.RoyalRoad.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RoyalRoad/Elib2Ebook.ExternalServices.RoyalRoad.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/RoyalRoadGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.RoyalRoad/RoyalRoadGetter.cs similarity index 92% rename from src/Core/Logic/Getters/RoyalRoadGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.RoyalRoad/RoyalRoadGetter.cs index fc91ed8d..62cb6580 100644 --- a/src/Core/Logic/Getters/RoyalRoadGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.RoyalRoad/RoyalRoadGetter.cs @@ -1,12 +1,13 @@ -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.RoyalRoad; public class RoyalRoadGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Elib2Ebook.ExternalServices.Rulate.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Elib2Ebook.ExternalServices.Rulate.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Elib2Ebook.ExternalServices.Rulate.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/Rulate/BllateGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters/BllateGetter.cs similarity index 67% rename from src/Core/Logic/Getters/Rulate/BllateGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters/BllateGetter.cs index f84afbc1..ccdc23c2 100644 --- a/src/Core/Logic/Getters/Rulate/BllateGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters/BllateGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.Rulate; +namespace Elib2Ebook.ExternalServices.Rulate.Getters; public class BllateGetter(BookGetterConfig config) : RulateGetterBase(config) { diff --git a/src/Core/Logic/Getters/Rulate/ErolateGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters/ErolateGetter.cs similarity index 72% rename from src/Core/Logic/Getters/Rulate/ErolateGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters/ErolateGetter.cs index 1fc03223..120e7667 100644 --- a/src/Core/Logic/Getters/Rulate/ErolateGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters/ErolateGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.Rulate; +namespace Elib2Ebook.ExternalServices.Rulate.Getters; public class ErolateGetter(BookGetterConfig config) : RulateGetterBase(config) { diff --git a/src/Core/Logic/Getters/Rulate/MlateGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters/MlateGetter.cs similarity index 71% rename from src/Core/Logic/Getters/Rulate/MlateGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters/MlateGetter.cs index 0027d68e..29587e31 100644 --- a/src/Core/Logic/Getters/Rulate/MlateGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters/MlateGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.Rulate; +namespace Elib2Ebook.ExternalServices.Rulate.Getters; public class MlateGetter(BookGetterConfig config) : RulateGetterBase(config) { diff --git a/src/Core/Logic/Getters/Rulate/RulateGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters/RulateGetter.cs similarity index 72% rename from src/Core/Logic/Getters/Rulate/RulateGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters/RulateGetter.cs index 098aa55c..58546f8e 100644 --- a/src/Core/Logic/Getters/Rulate/RulateGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters/RulateGetter.cs @@ -1,6 +1,6 @@ -using Core.Configs; +using Elib2Ebook.DomainServices.Configs; -namespace Core.Logic.Getters.Rulate; +namespace Elib2Ebook.ExternalServices.Rulate.Getters; public class RulateGetter(BookGetterConfig config) : RulateGetterBase(config) { diff --git a/src/Core/Logic/Getters/Rulate/RulateGetterBase.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters/RulateGetterBase.cs similarity index 94% rename from src/Core/Logic/Getters/Rulate/RulateGetterBase.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters/RulateGetterBase.cs index 0ca43773..5cd3ec3b 100644 --- a/src/Core/Logic/Getters/Rulate/RulateGetterBase.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Getters/RulateGetterBase.cs @@ -1,15 +1,16 @@ using System.Net; using System.Text.RegularExpressions; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.Rulate; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.Rulate.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.Rulate; +namespace Elib2Ebook.ExternalServices.Rulate.Getters; public abstract class RulateGetterBase(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Types/RulateAuthResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Types/RulateAuthResponse.cs new file mode 100644 index 00000000..8843479f --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Types/RulateAuthResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Rulate.Types; + +internal class RulateAuthResponse +{ + [JsonPropertyName("error")] + public string Error { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Types/RulateChapter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Types/RulateChapter.cs new file mode 100644 index 00000000..c6cd36c6 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Rulate/Types/RulateChapter.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Rulate.Types; + +internal class RulateChapter +{ + [JsonPropertyName("can_read")] + public bool CanRead { get; set; } + + [JsonPropertyName("content")] + public string Content { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Samlib/Elib2Ebook.ExternalServices.Samlib.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Samlib/Elib2Ebook.ExternalServices.Samlib.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Samlib/Elib2Ebook.ExternalServices.Samlib.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/SamlibGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Samlib/SamlibGetter.cs similarity index 95% rename from src/Core/Logic/Getters/SamlibGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Samlib/SamlibGetter.cs index 70877bea..8a5b3110 100644 --- a/src/Core/Logic/Getters/SamlibGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Samlib/SamlibGetter.cs @@ -1,13 +1,14 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Samlib; public class SamlibGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Elib2Ebook.ExternalServices.StrokiMts.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Elib2Ebook.ExternalServices.StrokiMts.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Elib2Ebook.ExternalServices.StrokiMts.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/StrokiMtsGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/StrokiMtsGetter.cs similarity index 96% rename from src/Core/Logic/Getters/StrokiMtsGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/StrokiMtsGetter.cs index caf90946..815bb816 100644 --- a/src/Core/Logic/Getters/StrokiMtsGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/StrokiMtsGetter.cs @@ -3,15 +3,15 @@ using System.Net.Http.Json; using System.Security.Cryptography; using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Misc; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.StrokiMts; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.StrokiMts.Types; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.StrokiMts; public class StrokiMtsGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsApiMultiResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsApiMultiResponse.cs new file mode 100644 index 00000000..65d48fb4 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsApiMultiResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.StrokiMts.Types; + +internal class StrokiMtsApiMultiResponse +{ + [JsonPropertyName("items")] + public List Items { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsApiResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsApiResponse.cs new file mode 100644 index 00000000..56cca895 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsApiResponse.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.StrokiMts.Types; + +internal class StrokiMtsApiResponse +{ + [JsonPropertyName("data")] + public T Data { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsAuthor.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsAuthor.cs new file mode 100644 index 00000000..fd28057b --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsAuthor.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.StrokiMts.Types; + +internal class StrokiMtsAuthor +{ + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("friendlyUrl")] + public string FriendlyUrl { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsBookItem.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsBookItem.cs new file mode 100644 index 00000000..38c2d5c3 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsBookItem.cs @@ -0,0 +1,24 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.StrokiMts.Types; + +internal class StrokiMtsBookItem +{ + [JsonPropertyName("id")] + public long Id { get; set; } + + [JsonPropertyName("annotation")] + public string Annotation { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("imageUrl")] + public Dictionary ImageUrl { get; set; } + + [JsonPropertyName("authors")] + public List Authors { get; set; } + + [JsonPropertyName("contentInfo")] + public StrokiMtsContentInfo ContentInfo { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsContentInfo.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsContentInfo.cs new file mode 100644 index 00000000..e1b792f7 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsContentInfo.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.StrokiMts.Types; + +internal class StrokiMtsContentInfo +{ + [JsonPropertyName("fileType")] + public string FileType { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsFile.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsFile.cs new file mode 100644 index 00000000..20082862 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsFile.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.StrokiMts.Types; + +internal class StrokiMtsFile +{ + [JsonPropertyName("fileId")] + public long FileId { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsFileUrl.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsFileUrl.cs new file mode 100644 index 00000000..a1585bb1 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsFileUrl.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.StrokiMts.Types; + +internal class StrokiMtsFileUrl +{ + [JsonPropertyName("url")] + public string Url { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsFiles.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsFiles.cs new file mode 100644 index 00000000..6408a9c9 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsFiles.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.StrokiMts.Types; + +internal class StrokiMtsFiles +{ + [JsonPropertyName("preview")] + public StrokiMtsFile Preview { get; set; } + + [JsonPropertyName("full")] + public StrokiMtsFile[] Full { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsMultiItem.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsMultiItem.cs new file mode 100644 index 00000000..8418819f --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.StrokiMts/Types/StrokiMtsMultiItem.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.StrokiMts.Types; + +internal class StrokiMtsMultiItem +{ + [JsonPropertyName("textBook")] + public StrokiMtsBookItem TextBook { get; set; } + + [JsonPropertyName("audioBook")] + public StrokiMtsBookItem AudioBook { get; set; } + + [JsonPropertyName("comics")] + public StrokiMtsBookItem ComicBook { get; set; } + + [JsonPropertyName("pressa")] + public StrokiMtsBookItem PressBook { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.TopLiba/Elib2Ebook.ExternalServices.TopLiba.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.TopLiba/Elib2Ebook.ExternalServices.TopLiba.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.TopLiba/Elib2Ebook.ExternalServices.TopLiba.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/TopLiba/BiglibaGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.TopLiba/Getters/BiglibaGetter.cs similarity index 86% rename from src/Core/Logic/Getters/TopLiba/BiglibaGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.TopLiba/Getters/BiglibaGetter.cs index 7053155f..b9887297 100644 --- a/src/Core/Logic/Getters/TopLiba/BiglibaGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.TopLiba/Getters/BiglibaGetter.cs @@ -1,11 +1,11 @@ -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; -namespace Core.Logic.Getters.TopLiba; +namespace Elib2Ebook.ExternalServices.TopLiba.Getters; public class BiglibaGetter(BookGetterConfig config) : TopLibaGetterBase(config) { diff --git a/src/Core/Logic/Getters/TopLiba/TopLibaGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.TopLiba/Getters/TopLibaGetter.cs similarity index 86% rename from src/Core/Logic/Getters/TopLiba/TopLibaGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.TopLiba/Getters/TopLibaGetter.cs index 44e76011..1c13d744 100644 --- a/src/Core/Logic/Getters/TopLiba/TopLibaGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.TopLiba/Getters/TopLibaGetter.cs @@ -1,11 +1,11 @@ -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; -namespace Core.Logic.Getters.TopLiba; +namespace Elib2Ebook.ExternalServices.TopLiba.Getters; public class TopLibaGetter(BookGetterConfig config) : TopLibaGetterBase(config) { diff --git a/src/Core/Logic/Getters/TopLiba/TopLibaGetterBase.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.TopLiba/Getters/TopLibaGetterBase.cs similarity index 95% rename from src/Core/Logic/Getters/TopLiba/TopLibaGetterBase.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.TopLiba/Getters/TopLibaGetterBase.cs index a635e6b8..8a44fb6b 100644 --- a/src/Core/Logic/Getters/TopLiba/TopLibaGetterBase.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.TopLiba/Getters/TopLibaGetterBase.cs @@ -1,13 +1,14 @@ using System.Text.RegularExpressions; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters.TopLiba; +namespace Elib2Ebook.ExternalServices.TopLiba.Getters; public abstract class TopLibaGetterBase(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.TwilightRussia/Elib2Ebook.ExternalServices.TwilightRussia.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.TwilightRussia/Elib2Ebook.ExternalServices.TwilightRussia.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.TwilightRussia/Elib2Ebook.ExternalServices.TwilightRussia.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/TwilightRussiaGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.TwilightRussia/TwilightRussiaGetter.cs similarity index 93% rename from src/Core/Logic/Getters/TwilightRussiaGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.TwilightRussia/TwilightRussiaGetter.cs index d0a43ba9..f88026de 100644 --- a/src/Core/Logic/Getters/TwilightRussiaGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.TwilightRussia/TwilightRussiaGetter.cs @@ -1,12 +1,13 @@ using System.Text; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.TwilightRussia; public class TwilightRussiaGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Elib2Ebook.ExternalServices.Wattpad.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Elib2Ebook.ExternalServices.Wattpad.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Elib2Ebook.ExternalServices.Wattpad.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Types/WattpadInfo.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Types/WattpadInfo.cs new file mode 100644 index 00000000..b97e5741 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Types/WattpadInfo.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Wattpad.Types; + +internal class WattpadInfo +{ + [JsonPropertyName("user")] + public WattpadUser User { get; set; } + + [JsonPropertyName("cover")] + public string Cover { get; set; } + + [JsonPropertyName("parts")] + public WattpadPart[] Parts { get; set; } + + [JsonPropertyName("description")] + public string Description { get; set; } + + [JsonPropertyName("title")] + public string Title { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Types/WattpadMeta.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Types/WattpadMeta.cs new file mode 100644 index 00000000..648e1d32 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Types/WattpadMeta.cs @@ -0,0 +1,6 @@ +namespace Elib2Ebook.ExternalServices.Wattpad.Types; + +internal class WattpadMeta +{ + public string StoryId { get; set; } +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Types/WattpadPart.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Types/WattpadPart.cs new file mode 100644 index 00000000..ccb469bd --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Types/WattpadPart.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; +using Elib2Ebook.DomainServices.Extensions; + +namespace Elib2Ebook.ExternalServices.Wattpad.Types; + +internal class WattpadPart +{ + [JsonPropertyName("id")] + public long Id { get; set; } + + [JsonPropertyName("title")] + public string Title { private get; set; } + + public string FullName => Title.Replace("\r", " ").CollapseWhitespace().Trim(); + + public Uri Url => new($"https://www.wattpad.com/apiv2/storytext?id={Id}"); +} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Types/WattpadUser.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Types/WattpadUser.cs new file mode 100644 index 00000000..7419f4dd --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/Types/WattpadUser.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.Wattpad.Types; + +internal class WattpadUser +{ + [JsonPropertyName("name")] + public string Name { get; set; } +} diff --git a/src/Core/Logic/Getters/WattpadGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/WattpadGetter.cs similarity index 90% rename from src/Core/Logic/Getters/WattpadGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/WattpadGetter.cs index 67a8b540..e2f78ecb 100644 --- a/src/Core/Logic/Getters/WattpadGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Wattpad/WattpadGetter.cs @@ -1,14 +1,15 @@ using System.Net.Http.Json; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.Wattpad; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.Wattpad.Types; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Wattpad; public class WattpadGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.WuxiaWorld/Elib2Ebook.ExternalServices.WuxiaWorld.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.WuxiaWorld/Elib2Ebook.ExternalServices.WuxiaWorld.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.WuxiaWorld/Elib2Ebook.ExternalServices.WuxiaWorld.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.WuxiaWorld/Types/WuxiaWorld/WuxiaWorldToc.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.WuxiaWorld/Types/WuxiaWorld/WuxiaWorldToc.cs new file mode 100644 index 00000000..46a206bd --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.WuxiaWorld/Types/WuxiaWorld/WuxiaWorldToc.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Elib2Ebook.ExternalServices.WuxiaWorld.Types.WuxiaWorld; + +internal class WuxiaWorldToc +{ + [JsonPropertyName("post_name")] + public string PostName { get; set; } +} diff --git a/src/Core/Logic/Getters/WuxiaWorldGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.WuxiaWorld/WuxiaWorldGetter.cs similarity index 93% rename from src/Core/Logic/Getters/WuxiaWorldGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.WuxiaWorld/WuxiaWorldGetter.cs index b27a646f..ddc300f5 100644 --- a/src/Core/Logic/Getters/WuxiaWorldGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.WuxiaWorld/WuxiaWorldGetter.cs @@ -1,16 +1,17 @@ using System.Net; using System.Net.Http.Json; using System.Text.RegularExpressions; -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; -using Core.Types.WuxiaWorld; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; +using Elib2Ebook.ExternalServices.WuxiaWorld.Types.WuxiaWorld; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.WuxiaWorld; public class WuxiaWorldGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Younettranslate/Elib2Ebook.ExternalServices.Younettranslate.csproj b/src/ExternalServices/Elib2Ebook.ExternalServices.Younettranslate/Elib2Ebook.ExternalServices.Younettranslate.csproj new file mode 100644 index 00000000..0b984323 --- /dev/null +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Younettranslate/Elib2Ebook.ExternalServices.Younettranslate.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Core/Logic/Getters/YounettranslateGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Younettranslate/YounettranslateGetter.cs similarity index 93% rename from src/Core/Logic/Getters/YounettranslateGetter.cs rename to src/ExternalServices/Elib2Ebook.ExternalServices.Younettranslate/YounettranslateGetter.cs index c02a4184..d91ee769 100644 --- a/src/Core/Logic/Getters/YounettranslateGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Younettranslate/YounettranslateGetter.cs @@ -1,12 +1,13 @@ -using Core.Configs; -using Core.Extensions; -using Core.Types.Book; -using Core.Types.Common; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.Domain.Common; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Extensions; +using Elib2Ebook.DomainServices.Getters; using HtmlAgilityPack; using HtmlAgilityPack.CssSelectors.NetCore; using Microsoft.Extensions.Logging; -namespace Core.Logic.Getters; +namespace Elib2Ebook.ExternalServices.Younettranslate; public class YounettranslateGetter(BookGetterConfig config) : GetterBase(config) { diff --git a/tests/Core.Tests/Extensions/ImageFormatExtensionsTests.cs b/tests/Core.Tests/Extensions/ImageFormatExtensionsTests.cs deleted file mode 100644 index 87ce811c..00000000 --- a/tests/Core.Tests/Extensions/ImageFormatExtensionsTests.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Core.Extensions; -using EpubSharp; -using EpubSharp.Format; - -namespace Core.Tests.Extensions; - -public class ImageFormatExtensionsTests -{ - [Theory] - [InlineData(ImageFormat.Gif, EpubContentType.ImageGif)] - [InlineData(ImageFormat.Png, EpubContentType.ImagePng)] - [InlineData(ImageFormat.Jpeg, EpubContentType.ImageJpeg)] - [InlineData(ImageFormat.Svg, EpubContentType.ImageSvg)] - public void ToEpubContentType(ImageFormat fmt, EpubContentType expected) - { - Assert.Equal(expected, fmt.ToEpubContentType()); - } - - [Fact] - public void ToEpubContentType_Unknown() - { - Assert.Throws(() => ((ImageFormat)999).ToEpubContentType()); - } -} diff --git a/tests/Core.Tests/Types/Book/BookTypeTests.cs b/tests/Core.Tests/Types/Book/BookTypeTests.cs deleted file mode 100644 index c994b4a8..00000000 --- a/tests/Core.Tests/Types/Book/BookTypeTests.cs +++ /dev/null @@ -1,165 +0,0 @@ -using Core.Misc; -using Core.Types.Common; -using TypesBook = Core.Types.Book; - -namespace Core.Tests.Types.Book; - -public class BookTypeTests -{ - private static TypesBook.Book MakeBook() - => new(new Uri("https://example.com/book")); - - private static async Task CreateTempFile(string dir, string name = "cover", string ext = ".jpg") - { - return await TempFile.Create( - new Uri($"https://example.com/{name}{ext}"), - dir, - name, - ext, - new byte[] - { - 1, 2, 3 - } - ); - } - - [Fact] - public void Book_CreateWithUrl_SetsUrl() - { - var url = new Uri("https://example.com/book"); - using var book = new TypesBook.Book(url); - Assert.Equal(url, book.Url); - } - - [Theory] - [InlineData("ru")] - [InlineData("en")] - public void Book_DefaultLanguage_IsRu(string lang) - { - using var book = new TypesBook.Book(new Uri("https://example.com/book")) - { - Lang = lang - }; - - Assert.Equal(lang, book.Lang); - } - - [Fact] - public void Book_DefaultChapters_IsEmpty() - { - using var book = MakeBook(); - Assert.NotNull(book.Chapters); - Assert.Empty(book.Chapters); - } - - [Fact] - public void Book_DefaultAdditionalFiles_IsEmpty() - { - using var book = MakeBook(); - Assert.NotNull(book.AdditionalFiles); - } - - [Fact] - public void Book_DefaultCoAuthors_IsEmpty() - { - using var book = MakeBook(); - Assert.NotNull(book.CoAuthors); - Assert.Empty(book.CoAuthors); - } - - [Fact] - public async Task Book_Dispose_CoverIsNullAfterDispose() - { - var book = new TypesBook.Book(new Uri("https://example.com/book")); - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(tempDir); - book.Cover = await CreateTempFile(tempDir); - book.Dispose(); - Assert.False(File.Exists(book.Cover.FilePath)); - Directory.Delete(tempDir); - } - - [Fact] - public void Author_Create_SetsProperties() - { - var author = new TypesBook.Author("Test Author", new Uri("https://example.com/author")); - Assert.Equal("Test Author", author.Name); - Assert.Equal("https://example.com/author", author.Url.ToString()); - } - - [Fact] - public void Author_NullUrl_DoesNotThrow() - { - var author = new TypesBook.Author("Test Author", null); - Assert.Equal("Test Author", author.Name); - Assert.Null(author.Url); - } - - [Theory] - [InlineData("Test Seria", "1", "https://example.com/seria")] - [InlineData("Empty Number", null, null)] - public void Seria_Create_SetsProperties(string name, string number, string url) - { - var seria = new TypesBook.Seria - { - Name = name, Number = number, Url = url is null ? null : new Uri(url) - }; - - Assert.Equal(name, seria.Name); - Assert.Equal(number, seria.Number); - if (url is null) - { - Assert.Null(seria.Url); - } - else - { - Assert.NotNull(seria.Url); - } - } - - [Theory] - [InlineData("Chapter 1", "Content here")] - [InlineData("", "")] - public void Chapter_Create_SetsProperties(string title, string content) - { - var chapter = new TypesBook.Chapter - { - Title = title, Content = content - }; - - Assert.Equal(title, chapter.Title); - Assert.Equal(content, chapter.Content); - } - - [Fact] - public void AdditionalFileCollection_DefaultCollection_IsEmpty() - { - var collection = new TypesBook.AdditionalFileCollection(); - Assert.NotNull(collection); - Assert.Empty(collection.Collection); - } - - [Fact] - public async Task AdditionalFileCollection_AddFile_StoresFile() - { - var collection = new TypesBook.AdditionalFileCollection(); - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(tempDir); - - var tempFile = await CreateTempFile(tempDir, "file", ".jpg"); - - collection.Add(AdditionalTypeEnum.Images, tempFile); - var files = collection.Get(AdditionalTypeEnum.Images); - Assert.Single(files); - Assert.Same(tempFile, files[0]); - Directory.Delete(tempDir, true); - } - - [Fact] - public void AdditionalFileCollection_GetNonExistentType_ReturnsEmpty() - { - var collection = new TypesBook.AdditionalFileCollection(); - var files = collection.Get(AdditionalTypeEnum.Images); - Assert.Empty(files); - } -} diff --git a/tests/Core.Tests/Core.Tests.csproj b/tests/Elib2Ebook.DomainServices.Tests/Elib2Ebook.DomainServices.Tests.csproj similarity index 57% rename from tests/Core.Tests/Core.Tests.csproj rename to tests/Elib2Ebook.DomainServices.Tests/Elib2Ebook.DomainServices.Tests.csproj index 55f07e21..50b87465 100644 --- a/tests/Core.Tests/Core.Tests.csproj +++ b/tests/Elib2Ebook.DomainServices.Tests/Elib2Ebook.DomainServices.Tests.csproj @@ -6,11 +6,12 @@ + - all - runtime; build; native; contentfiles; analyzers; buildtransitive + all + runtime; build; native; contentfiles; analyzers; buildtransitive @@ -19,13 +20,6 @@ - + - - - - ..\..\src\Core\External\EpubSharp.dll - - - diff --git a/tests/Core.Tests/Extensions/HtmlDocumentExtensionsTests.cs b/tests/Elib2Ebook.DomainServices.Tests/Extensions/HtmlDocumentExtensionsTests.cs similarity index 96% rename from tests/Core.Tests/Extensions/HtmlDocumentExtensionsTests.cs rename to tests/Elib2Ebook.DomainServices.Tests/Extensions/HtmlDocumentExtensionsTests.cs index 5ec5b897..12f1caf9 100644 --- a/tests/Core.Tests/Extensions/HtmlDocumentExtensionsTests.cs +++ b/tests/Elib2Ebook.DomainServices.Tests/Extensions/HtmlDocumentExtensionsTests.cs @@ -1,7 +1,7 @@ -using Core.Extensions; +using Elib2Ebook.DomainServices.Extensions; using HtmlAgilityPack; -namespace Core.Tests.Extensions; +namespace Elib2Ebook.DomainServices.Tests.Extensions; public class HtmlDocumentExtensionsTests { diff --git a/tests/Core.Tests/Extensions/StreamExtensionTests.cs b/tests/Elib2Ebook.DomainServices.Tests/Extensions/StreamExtensionTests.cs similarity index 88% rename from tests/Core.Tests/Extensions/StreamExtensionTests.cs rename to tests/Elib2Ebook.DomainServices.Tests/Extensions/StreamExtensionTests.cs index f9bad3ae..4c116f25 100644 --- a/tests/Core.Tests/Extensions/StreamExtensionTests.cs +++ b/tests/Elib2Ebook.DomainServices.Tests/Extensions/StreamExtensionTests.cs @@ -1,7 +1,7 @@ using System.Text; -using Core.Extensions; +using Elib2Ebook.DomainServices.Extensions; -namespace Core.Tests.Extensions; +namespace Elib2Ebook.DomainServices.Tests.Extensions; public class StreamExtensionTests { diff --git a/tests/Core.Tests/Extensions/StringBuilderExtensionsTests.cs b/tests/Elib2Ebook.DomainServices.Tests/Extensions/StringBuilderExtensionsTests.cs similarity index 76% rename from tests/Core.Tests/Extensions/StringBuilderExtensionsTests.cs rename to tests/Elib2Ebook.DomainServices.Tests/Extensions/StringBuilderExtensionsTests.cs index 1019dcce..600e78b7 100644 --- a/tests/Core.Tests/Extensions/StringBuilderExtensionsTests.cs +++ b/tests/Elib2Ebook.DomainServices.Tests/Extensions/StringBuilderExtensionsTests.cs @@ -1,7 +1,7 @@ using System.Text; -using Core.Extensions; +using Elib2Ebook.DomainServices.Extensions; -namespace Core.Tests.Extensions; +namespace Elib2Ebook.DomainServices.Tests.Extensions; public class StringBuilderExtensionsTests { diff --git a/tests/Core.Tests/Extensions/StringExtensionsTests.cs b/tests/Elib2Ebook.DomainServices.Tests/Extensions/StringExtensionsTests.cs similarity index 96% rename from tests/Core.Tests/Extensions/StringExtensionsTests.cs rename to tests/Elib2Ebook.DomainServices.Tests/Extensions/StringExtensionsTests.cs index 88f80b90..f3177af5 100644 --- a/tests/Core.Tests/Extensions/StringExtensionsTests.cs +++ b/tests/Elib2Ebook.DomainServices.Tests/Extensions/StringExtensionsTests.cs @@ -1,6 +1,6 @@ -using Core.Extensions; +using Elib2Ebook.DomainServices.Extensions; -namespace Core.Tests.Extensions; +namespace Elib2Ebook.DomainServices.Tests.Extensions; public class StringExtensionsTests { diff --git a/tests/Core.Tests/Extensions/UriExtensionTests.cs b/tests/Elib2Ebook.DomainServices.Tests/Extensions/UriExtensionTests.cs similarity index 95% rename from tests/Core.Tests/Extensions/UriExtensionTests.cs rename to tests/Elib2Ebook.DomainServices.Tests/Extensions/UriExtensionTests.cs index 08c8366b..6e19e111 100644 --- a/tests/Core.Tests/Extensions/UriExtensionTests.cs +++ b/tests/Elib2Ebook.DomainServices.Tests/Extensions/UriExtensionTests.cs @@ -1,6 +1,6 @@ -using Core.Extensions; +using Elib2Ebook.DomainServices.Extensions; -namespace Core.Tests.Extensions; +namespace Elib2Ebook.DomainServices.Tests.Extensions; public class UriExtensionTests { diff --git a/tests/Core.Tests/Logic/Builders/BuilderTests.cs b/tests/Elib2Ebook.DomainServices.Tests/Logic/Builders/BuilderTests.cs similarity index 88% rename from tests/Core.Tests/Logic/Builders/BuilderTests.cs rename to tests/Elib2Ebook.DomainServices.Tests/Logic/Builders/BuilderTests.cs index a43e2037..833a3bf0 100644 --- a/tests/Core.Tests/Logic/Builders/BuilderTests.cs +++ b/tests/Elib2Ebook.DomainServices.Tests/Logic/Builders/BuilderTests.cs @@ -1,8 +1,8 @@ -using Core.Configs; -using Core.Logic.Builders; +using Elib2Ebook.DomainServices.Builders; +using Elib2Ebook.DomainServices.Configs; using Microsoft.Extensions.Logging.Abstractions; -namespace Core.Tests.Logic.Builders; +namespace Elib2Ebook.DomainServices.Tests.Logic.Builders; public class BuilderTests { diff --git a/tests/Core.Tests/Misc/MiscTests.cs b/tests/Elib2Ebook.DomainServices.Tests/Misc/MiscTests.cs similarity index 91% rename from tests/Core.Tests/Misc/MiscTests.cs rename to tests/Elib2Ebook.DomainServices.Tests/Misc/MiscTests.cs index 365fd038..667e2c61 100644 --- a/tests/Core.Tests/Misc/MiscTests.cs +++ b/tests/Elib2Ebook.DomainServices.Tests/Misc/MiscTests.cs @@ -1,11 +1,10 @@ -using Core.Configs; -using Core.Logic; -using Core.Logic.Builders; -using Core.Misc; -using Core.Types.Book; +using Elib2Ebook.Domain.Book; +using Elib2Ebook.DomainServices.Builders; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Misc; using Microsoft.Extensions.Logging.Abstractions; -namespace Core.Tests.Misc; +namespace Elib2Ebook.DomainServices.Tests.Misc; public class BuilderProviderTests { diff --git a/tests/Core.Tests/Misc/TempFolderTests.cs b/tests/Elib2Ebook.DomainServices.Tests/Misc/TempFolderTests.cs similarity index 96% rename from tests/Core.Tests/Misc/TempFolderTests.cs rename to tests/Elib2Ebook.DomainServices.Tests/Misc/TempFolderTests.cs index f6bc2423..2dc0507a 100644 --- a/tests/Core.Tests/Misc/TempFolderTests.cs +++ b/tests/Elib2Ebook.DomainServices.Tests/Misc/TempFolderTests.cs @@ -1,6 +1,6 @@ -using Core.Misc.TempFolder; +using Elib2Ebook.DomainServices.Misc.TempFolder; -namespace Core.Tests.Misc; +namespace Elib2Ebook.DomainServices.Tests.Misc; public class TempFolderTests { From 4f5f9b1c7d64fe6ff7bbf163e0f5f72e2e344c46 Mon Sep 17 00:00:00 2001 From: OnlyFart Date: Wed, 8 Jul 2026 15:41:58 +0300 Subject: [PATCH 07/29] Refactor (#182) --- .github/workflows/dotnet.yml | 4 +- Directory.Packages.props | 3 +- .../Elib2Ebook.DomainServices.csproj | 1 + .../Misc/GetterFactory.cs | 70 +++++++++++-------- .../Misc/GetterProvider.cs | 60 ++++++++++++++++ src/Elib2EbookCli/Program.cs | 2 +- 6 files changed, 105 insertions(+), 35 deletions(-) create mode 100644 src/Elib2Ebook.DomainServices/Misc/GetterProvider.cs diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index af2c89d8..e9190772 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -67,13 +67,13 @@ jobs: - name: Min run: > dotnet publish -c release -p:PublishDir=../../dist/min --no-self-contained - -p:PublishSingleFile=true -p:DebugType=embedded -p:PublishTrimmed=false + -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -p:DebugType=embedded -p:PublishTrimmed=false -p:EnableCompressionInSingleFile=false --use-current-runtime src/Elib2EbookCli/Elib2EbookCli.csproj - name: Portable run: > dotnet publish -c release -p:PublishDir=../../dist/portable --self-contained - -p:PublishSingleFile=true -p:DebugType=embedded + -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -p:DebugType=embedded -p:EnableCompressionInSingleFile=true --use-current-runtime src/Elib2EbookCli/Elib2EbookCli.csproj - uses: actions/upload-artifact@v7 diff --git a/Directory.Packages.props b/Directory.Packages.props index 2ebf25a3..90107a0c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -15,6 +15,7 @@ + @@ -23,4 +24,4 @@ - \ No newline at end of file + diff --git a/src/Elib2Ebook.DomainServices/Elib2Ebook.DomainServices.csproj b/src/Elib2Ebook.DomainServices/Elib2Ebook.DomainServices.csproj index 46496764..9d0ad4b6 100644 --- a/src/Elib2Ebook.DomainServices/Elib2Ebook.DomainServices.csproj +++ b/src/Elib2Ebook.DomainServices/Elib2Ebook.DomainServices.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Elib2Ebook.DomainServices/Misc/GetterFactory.cs b/src/Elib2Ebook.DomainServices/Misc/GetterFactory.cs index 3202df76..4ec381a5 100644 --- a/src/Elib2Ebook.DomainServices/Misc/GetterFactory.cs +++ b/src/Elib2Ebook.DomainServices/Misc/GetterFactory.cs @@ -1,58 +1,66 @@ using System.Reflection; +using System.Runtime.Loader; using Elib2Ebook.DomainServices.Configs; using Elib2Ebook.DomainServices.Getters; +using Microsoft.Extensions.DependencyModel; namespace Elib2Ebook.DomainServices.Misc; public static class GetterFactory { - private static IEnumerable GetGetterTypes() + private static IReadOnlySet GetAssemblies() { - LoadAllAssemblies(); - - return AppDomain.CurrentDomain.GetAssemblies() - .SelectMany(a => - { - try - { - return a.GetTypes(); - } - catch - { - return []; - } - }) - .Where(myType => myType is { IsClass: true, IsAbstract: false } && myType.IsSubclassOf(typeof(GetterBase))); - } + var result = AppDomain.CurrentDomain.GetAssemblies().ToHashSet(); + var entry = Assembly.GetEntryAssembly(); + if (entry == null) + { + return result; + } - private static void LoadAllAssemblies() - { - var loadedAssemblyPaths = AppDomain.CurrentDomain.GetAssemblies() - .Select(a => a.Location) - .Where(p => !string.IsNullOrEmpty(p)) - .ToHashSet(StringComparer.OrdinalIgnoreCase); + var dependencyContext = DependencyContext.Load(entry); - var baseDir = AppDomain.CurrentDomain.BaseDirectory; + const string names = "Elib2Ebook.ExternalServices"; + var assemblyNames = + dependencyContext != null ? + dependencyContext.RuntimeLibraries + .Where(a => a.Name.StartsWith(names)) + .Select(l => new AssemblyName(l.Name)) : + entry.GetReferencedAssemblies() + .Where(a => a.Name != null && a.Name.StartsWith(names)); - foreach (var dllPath in Directory.EnumerateFiles(baseDir, "Elib2Ebook.ExternalServices.*.dll", SearchOption.AllDirectories)) + var alc = AssemblyLoadContext.Default; + foreach (var assemblyName in assemblyNames) { - if (loadedAssemblyPaths.Contains(dllPath)) - continue; - try { - Assembly.LoadFrom(dllPath); + result.Add(alc.LoadFromAssemblyName(assemblyName)); } catch { - // ignore assemblies that fail to load + // игнорируем } } + + return result; + } + + private static IEnumerable GetGetterTypesFromAssembly(Assembly asm) + { + try + { + return asm.GetTypes() + .Where(myType => myType is { IsClass: true, IsAbstract: false } && myType.IsSubclassOf(typeof(GetterBase))); + } + catch + { + return []; + } } public static GetterBase Get(BookGetterConfig config, Uri url) { - return GetGetterTypes() + return GetAssemblies() + .SelectMany(GetGetterTypesFromAssembly) .Select(type => (GetterBase)Activator.CreateInstance(type, config)) .FirstOrDefault(g => g!.IsSameUrl(url)) ?? throw new ArgumentException($"Сайт {url.Host} не поддерживается"); diff --git a/src/Elib2Ebook.DomainServices/Misc/GetterProvider.cs b/src/Elib2Ebook.DomainServices/Misc/GetterProvider.cs new file mode 100644 index 00000000..e87cbd7b --- /dev/null +++ b/src/Elib2Ebook.DomainServices/Misc/GetterProvider.cs @@ -0,0 +1,60 @@ +using System.Reflection; +using Elib2Ebook.DomainServices.Configs; +using Elib2Ebook.DomainServices.Getters; + +namespace Elib2Ebook.DomainServices.Misc; + +public static class GetterProvider +{ + private static IEnumerable GetGetterTypes() + { + LoadAllAssemblies(); + + return AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(a => + { + try + { + return a.GetTypes(); + } + catch + { + return []; + } + }) + .Where(myType => myType is { IsClass: true, IsAbstract: false } && myType.IsSubclassOf(typeof(GetterBase))); + } + + private static void LoadAllAssemblies() + { + var loadedAssemblyPaths = AppDomain.CurrentDomain.GetAssemblies() + .Select(a => a.Location) + .Where(p => !string.IsNullOrEmpty(p)) + .ToHashSet(StringComparer.OrdinalIgnoreCase); + + var baseDir = AppDomain.CurrentDomain.BaseDirectory; + + foreach (var dllPath in Directory.EnumerateFiles(baseDir, "Elib2Ebook.ExternalServices.*.dll", SearchOption.AllDirectories)) + { + if (loadedAssemblyPaths.Contains(dllPath)) + continue; + + try + { + Assembly.LoadFrom(dllPath); + } + catch + { + // ignore assemblies that fail to load + } + } + } + + public static GetterBase Get(BookGetterConfig config, Uri url) + { + return GetGetterTypes() + .Select(type => (GetterBase)Activator.CreateInstance(type, config)) + .FirstOrDefault(g => g!.IsSameUrl(url)) ?? + throw new ArgumentException($"Сайт {url.Host} не поддерживается"); + } +} diff --git a/src/Elib2EbookCli/Program.cs b/src/Elib2EbookCli/Program.cs index 7f7c8cfb..f83fd7bd 100644 --- a/src/Elib2EbookCli/Program.cs +++ b/src/Elib2EbookCli/Program.cs @@ -28,7 +28,7 @@ await parserResult .WithNotParsed(errs => { var title = AppDomain.CurrentDomain.FriendlyName; - var coreAssembly = typeof(BookGetterConfig).Assembly; + var coreAssembly = typeof(Program).Assembly; var version = coreAssembly.GetCustomAttribute()?.Version ?? string.Empty; var heading = new HeadingInfo(title, version); From 888f08c60a0e3aa06465f8903e2fe0d0d0b2e2e2 Mon Sep 17 00:00:00 2001 From: olekoloskov Date: Thu, 9 Jul 2026 14:56:25 +0300 Subject: [PATCH 08/29] https://github.com/OnlyFart/Elib2Ebook/issues/183 --- Directory.Build.props | 2 +- .../Book/AdditionalFileCollection.cs | 2 +- .../Builders/EpubBuilder.cs | 4 ++-- .../Builders/Fb2Builder.cs | 2 +- src/Elib2EbookWeb/Components/BookForm.razor | 8 ++++---- .../AcomicsGetter.cs | 8 ++++---- .../HubBookGetter.cs | 8 ++++---- .../LitmarketGetter.cs | 7 ++++++- .../Types/LitresPayload.cs | 2 +- .../MirKnigGetter.cs | 8 ++++---- .../OnlineKnigiGetter.cs | 8 ++++---- .../RomfantGetter.cs | 8 ++++---- .../Logic/Builders/BuilderTests.cs | 15 +++++++-------- .../Misc/MiscTests.cs | 14 ++++++-------- 14 files changed, 49 insertions(+), 47 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 297468eb..3caa5012 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,4 +6,4 @@ latestmajor 2.8.2 - \ No newline at end of file + diff --git a/src/Elib2Ebook.Domain/Book/AdditionalFileCollection.cs b/src/Elib2Ebook.Domain/Book/AdditionalFileCollection.cs index fb1539ca..413c0662 100644 --- a/src/Elib2Ebook.Domain/Book/AdditionalFileCollection.cs +++ b/src/Elib2Ebook.Domain/Book/AdditionalFileCollection.cs @@ -10,7 +10,7 @@ public void Add(AdditionalTypeEnum type, TempFile file) { if (!Collection.TryGetValue(type, out var files)) { - files = new(); + files = []; Collection[type] = files; } diff --git a/src/Elib2Ebook.DomainServices/Builders/EpubBuilder.cs b/src/Elib2Ebook.DomainServices/Builders/EpubBuilder.cs index 876c9831..9fe3e7bf 100644 --- a/src/Elib2Ebook.DomainServices/Builders/EpubBuilder.cs +++ b/src/Elib2Ebook.DomainServices/Builders/EpubBuilder.cs @@ -14,7 +14,7 @@ public class EpubBuilder(Options options, ILogger logger) : BuilderBase(options, private readonly EpubWriter _writer = new(); - private List Images { get; set; } = new(); + private List Images { get; set; } = []; /// /// Создание Xhtml документа из кода части @@ -97,7 +97,7 @@ private void Write(Book book) { foreach (var image in chapter.Images) { - _writer.AddFile(image.FullName, Array.Empty(), GetImageFormat(image.FullName).ToEpubContentType()); + _writer.AddFile(image.FullName, [], GetImageFormat(image.FullName).ToEpubContentType()); Images.Add(image); } diff --git a/src/Elib2Ebook.DomainServices/Builders/Fb2Builder.cs b/src/Elib2Ebook.DomainServices/Builders/Fb2Builder.cs index 6ec0e72c..27559d6b 100644 --- a/src/Elib2Ebook.DomainServices/Builders/Fb2Builder.cs +++ b/src/Elib2Ebook.DomainServices/Builders/Fb2Builder.cs @@ -21,7 +21,7 @@ public class Fb2Builder : BuilderBase private readonly XElement _titleInfo; private readonly XElement _body; private readonly XElement _documentInfo; - private readonly List _images = new(); + private readonly List _images = []; private readonly Dictionary _map = new() { diff --git a/src/Elib2EbookWeb/Components/BookForm.razor b/src/Elib2EbookWeb/Components/BookForm.razor index 900c2f44..7e2ea421 100644 --- a/src/Elib2EbookWeb/Components/BookForm.razor +++ b/src/Elib2EbookWeb/Components/BookForm.razor @@ -7,10 +7,10 @@ + Variant="Variant.Outlined" TextChanged="@(value => _options.Url = +[ + value, +])" AutoFocus="true"/> > FillChapters(HtmlDocument doc, string b chapter.Images = await GetImages(chapterDoc, url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; - return new[] - { - chapter - }; + return + [ + chapter, + ]; } } diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.HubBook/HubBookGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.HubBook/HubBookGetter.cs index 28a47eea..e8b4d1c2 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.HubBook/HubBookGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.HubBook/HubBookGetter.cs @@ -61,10 +61,10 @@ private async Task> FillChapters(HtmlDocument doc, Uri url, chapter.Images = await GetImages(chapterDoc, url); chapter.Content = chapterDoc.DocumentNode.InnerHtml; - return new[] - { - chapter - }; + return + [ + chapter, + ]; } private Task GetCover(HtmlDocument doc, Uri uri) diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/LitmarketGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/LitmarketGetter.cs index a7265683..9ef94910 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/LitmarketGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/LitmarketGetter.cs @@ -126,7 +126,7 @@ public override async Task Authorize() private Task GetCover(HtmlDocument doc, Uri uri) { - var imagePath = doc.QuerySelector("div.front img")?.Attributes["data-src"]?.Value; + var imagePath = doc.QuerySelector("img.img.bookCardSkeleton")?.Attributes["src"]?.Value; return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath.AsUri().AbsolutePath)) : Task.FromResult(default(TempFile)); } @@ -188,6 +188,11 @@ private async Task> FillChapters(List toc, Block[] blocks, break; default: var text = $"{mod.Text ?? string.Empty}"; + if (sb.Length == 0 && text == chapterTitle) + { + continue; + } + if (mod.Styles?.Length > 0) { foreach (var style in mod.Styles) diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/LitresPayload.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/LitresPayload.cs index f3c8c748..71f06068 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/LitresPayload.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/LitresPayload.cs @@ -22,7 +22,7 @@ internal class LitresPayload public string App { get; set; } [JsonPropertyName("requests")] - public List Requests { get; set; } = new(); + public List Requests { get; set; } = []; public static LitresPayload Create(DateTime date, string sid, string secret, string app) { diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.MirKnig/MirKnigGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.MirKnig/MirKnigGetter.cs index 584db1f6..fe4e5583 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.MirKnig/MirKnigGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.MirKnig/MirKnigGetter.cs @@ -61,10 +61,10 @@ private async Task> FillChapters(string bookId, string titl Title = title, Images = await GetImages(doc, SystemUrl), Content = doc.DocumentNode.InnerHtml }; - return new[] - { - chapter - }; + return + [ + chapter, + ]; } private static Author GetAuthor(HtmlDocument doc, Uri url) diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.OnlineKnigi/OnlineKnigiGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.OnlineKnigi/OnlineKnigiGetter.cs index c7849c47..6e9ede91 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.OnlineKnigi/OnlineKnigiGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.OnlineKnigi/OnlineKnigiGetter.cs @@ -61,10 +61,10 @@ private async Task> FillChapters(string bookId, string titl chapter.Title = title; chapter.Content = chapterDoc.DocumentNode.InnerHtml; - return new[] - { - chapter - }; + return + [ + chapter, + ]; } private async Task GetPages(string bookId) diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Romfant/RomfantGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Romfant/RomfantGetter.cs index 1212ee9e..bafd9dbd 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.Romfant/RomfantGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Romfant/RomfantGetter.cs @@ -109,10 +109,10 @@ private async Task> FillChapters(string bookId, string titl chapter.Title = title; chapter.Content = sb.AsHtmlDoc().DocumentNode.InnerHtml; - return new[] - { - chapter - }; + return + [ + chapter, + ]; } private async Task GetPages(string bookId) diff --git a/tests/Elib2Ebook.DomainServices.Tests/Logic/Builders/BuilderTests.cs b/tests/Elib2Ebook.DomainServices.Tests/Logic/Builders/BuilderTests.cs index 833a3bf0..00f72292 100644 --- a/tests/Elib2Ebook.DomainServices.Tests/Logic/Builders/BuilderTests.cs +++ b/tests/Elib2Ebook.DomainServices.Tests/Logic/Builders/BuilderTests.cs @@ -8,15 +8,14 @@ public class BuilderTests { private static Options Opt(string format) => new( - new[] - { - "https://example.com/book" - }) + [ + "https://example.com/book", + ]) { - Format = new[] - { - format - }, + Format = + [ + format, + ], BookNamePattern = "{Book.Title}", Timeout = 120 }; diff --git a/tests/Elib2Ebook.DomainServices.Tests/Misc/MiscTests.cs b/tests/Elib2Ebook.DomainServices.Tests/Misc/MiscTests.cs index 667e2c61..19ff4b18 100644 --- a/tests/Elib2Ebook.DomainServices.Tests/Misc/MiscTests.cs +++ b/tests/Elib2Ebook.DomainServices.Tests/Misc/MiscTests.cs @@ -22,10 +22,9 @@ public void Get(string format, Type type) BuilderProvider.Get( format, new Options( - new[] - { - "https://x.com" - }), + [ + "https://x.com", + ]), NullLogger.Instance)); } @@ -35,10 +34,9 @@ public void Get_Invalid() Assert.Throws(() => BuilderProvider.Get( "bad", new Options( - new[] - { - "https://x.com" - }), + [ + "https://x.com", + ]), NullLogger.Instance)); } } From 683fb285bedfbf17028029b17693151ed9139721 Mon Sep 17 00:00:00 2001 From: olekoloskov Date: Thu, 9 Jul 2026 15:05:33 +0300 Subject: [PATCH 09/29] CodeQl --- .github/workflows/codeql.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 937667a5..90f4d6ab 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@v5 - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} queries: security-and-quality @@ -43,6 +43,6 @@ jobs: run: dotnet build -c release Elib2Ebook.sln - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v4 with: - category: "/language:${{matrix.language}}" \ No newline at end of file + category: "/language:${{matrix.language}}" From d193dcce772ec36ef6462caa658e8bfbdd7988aa Mon Sep 17 00:00:00 2001 From: olekoloskov Date: Thu, 9 Jul 2026 17:03:13 +0300 Subject: [PATCH 10/29] fix --- .../Elib2Ebook.ExternalServices.Litmarket/LitmarketGetter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/LitmarketGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/LitmarketGetter.cs index 9ef94910..2bc4b961 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/LitmarketGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/LitmarketGetter.cs @@ -127,7 +127,7 @@ public override async Task Authorize() private Task GetCover(HtmlDocument doc, Uri uri) { var imagePath = doc.QuerySelector("img.img.bookCardSkeleton")?.Attributes["src"]?.Value; - return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath.AsUri().AbsolutePath)) : Task.FromResult(default(TempFile)); + return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } private List GetToc(Response response, string title) From f8cacf0e809eb000b822145ce76857e8c6ca2da0 Mon Sep 17 00:00:00 2001 From: olekoloskov Date: Thu, 9 Jul 2026 17:03:13 +0300 Subject: [PATCH 11/29] fix *lib.me --- .../Getters/MangaLibGetter.cs | 2 +- .../Getters/MangaLibGetterBase.cs | 2 +- .../Getters/NewLibSocialGetterBase.cs | 131 +++++++++++++++++- .../Getters/RanobeLibGetter.cs | 116 +--------------- .../Types/SocialLib/RanobeLibBookDetail.cs | 3 +- .../Types/SocialLib/SocialLibBookChapters.cs | 6 +- .../LitmarketGetter.cs | 2 +- 7 files changed, 137 insertions(+), 125 deletions(-) diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/MangaLibGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/MangaLibGetter.cs index cd0ba323..127a2dbd 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/MangaLibGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/MangaLibGetter.cs @@ -4,7 +4,7 @@ namespace Elib2Ebook.ExternalServices.LibSocial.Getters; public class MangaLibGetter(BookGetterConfig config) : MangaLibGetterBase(config) { - protected override Uri ImagesHost => new("https://img33.imgslib.link/"); + protected override Uri ImagesHost => new("https://img3.cdnlibs.org/"); protected override Uri SystemUrl => new("https://mangalib.me/"); diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/MangaLibGetterBase.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/MangaLibGetterBase.cs index 32f58ed0..4ad6041c 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/MangaLibGetterBase.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/MangaLibGetterBase.cs @@ -14,7 +14,7 @@ protected override HtmlDocument ResponseToHtmlDoc(SocialLibBookChapter chapterRe foreach (var page in chapterResponse.Pages) { - var url = SystemUrl.MakeRelativeUri(page.Url.TrimStart('/')); + var url = ImagesHost.MakeRelativeUri(page.Url.TrimStart('/')); sb.Append($""); } diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/NewLibSocialGetterBase.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/NewLibSocialGetterBase.cs index 3406d7ef..99c79b9c 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/NewLibSocialGetterBase.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/NewLibSocialGetterBase.cs @@ -3,6 +3,8 @@ using System.Net.Http.Json; using System.Security.Cryptography; using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; using Elib2Ebook.Domain.Book; using Elib2Ebook.Domain.Common; using Elib2Ebook.DomainServices.Configs; @@ -30,6 +32,48 @@ public abstract class NewLibSocialGetterBase(BookGetterConfig config) : GetterBa protected abstract int SiteId { get; } + private static readonly Dictionary RecursiveTag = new() + { + { + "paragraph", "p" + }, + { + "orderedList", "ol" + }, + { + "bulletList", "ul" + }, + { + "listItem", "li" + }, + { + "blockquote", "blockquote" + }, + }; + + private static readonly Dictionary InlineTag = new() + { + { + "horizontalRule", "
" + }, + { + "hardBreak", "
" + }, + }; + + private static readonly Dictionary MarkTag = new() + { + { + "italic", "i" + }, + { + "bold", "b" + }, + { + "underline", "u" + }, + }; + public override async Task Init() { await base.Init(); @@ -222,7 +266,7 @@ public override async Task Get(Uri url) Title = string.IsNullOrWhiteSpace(details.Data.RusName) ? details.Data.Name : details.Data.RusName, Author = GetAuthor(details), CoAuthors = GetCoAuthors(details), - Annotation = details.Data.Summary + Annotation = GetDescription(details).DocumentNode.InnerHtml, }; return book; @@ -298,8 +342,6 @@ private Task GetCover(RanobeLibBookDetails details) protected override HttpRequestMessage GetImageRequestMessage(Uri uri) { - uri = uri.Host == BaseImageHost.Host ? uri : uri.ReplaceHost(ImagesHost.Host); - var message = base.GetImageRequestMessage(uri); message.Headers.Add("Referer", SystemUrl.ToString()); return message; @@ -320,7 +362,7 @@ private async Task> FillChapters(RanobeLibBookDetails book, var chapter = new Chapter { - Title = title + Title = title, }; var chapterDoc = await GetChapter(book, socialChapter, bid); @@ -345,5 +387,86 @@ private async Task GetChapter(RanobeLibBookDetails book, SocialLib return ResponseToHtmlDoc(chapterResponse.Data); } + protected StringBuilder AsHtml(SocialLibChapterAttachment[] attachments, IEnumerable contents) + { + var sb = new StringBuilder(); + + foreach (var content in contents) + { + if (RecursiveTag.TryGetValue(content.Type, out var tag)) + { + sb.Append(AsHtml(attachments, content.Content).ToString().CoverTag(tag)); + continue; + } + + if (InlineTag.TryGetValue(content.Type, out tag)) + { + sb.Append(tag); + continue; + } + + switch (content.Type) + { + case "text": + { + var text = content.Text.HtmlEncode(); + + foreach (var mark in content.Marks) + { + if (MarkTag.TryGetValue(mark.Type, out tag)) + { + text = text.CoverTag(tag); + } + else + { + Config.Logger.LogInformation($"Неизвестый тип форматирования {mark.Type}"); + } + } + + sb.Append(text); + continue; + } + case "image": + { + if (content.Attrs.TryGetValue("images", out var images)) + { + foreach (var image in images.Deserialize[]>()) + { + if (!image.TryGetValue("image", out var imageId)) + { + continue; + } + + var attachment = attachments.FirstOrDefault(a => a.Name == imageId); + if (attachment == null) + { + continue; + } + + sb.Append($""); + } + } + + continue; + } + default: + Config.Logger.LogInformation($"Неизвестый тип {content.Type}"); + break; + } + } + + return sb; + } + + private HtmlDocument GetDescription(RanobeLibBookDetails details) + { + return details.Data.Summary switch + { + JsonValue e => e.GetValue().AsHtmlDoc(), + JsonObject o => AsHtml([], o.Deserialize().Content).AsHtmlDoc(), + _ => throw new Exception("Неизвестный тип"), + }; + } + protected abstract HtmlDocument ResponseToHtmlDoc(SocialLibBookChapter chapterResponse); } diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/RanobeLibGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/RanobeLibGetter.cs index 077ef4dd..8d255caf 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/RanobeLibGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Getters/RanobeLibGetter.cs @@ -1,11 +1,9 @@ -using System.Text; using System.Text.Json; using System.Text.Json.Nodes; using Elib2Ebook.DomainServices.Configs; using Elib2Ebook.DomainServices.Extensions; using Elib2Ebook.ExternalServices.LibSocial.Types.SocialLib; using HtmlAgilityPack; -using Microsoft.Extensions.Logging; namespace Elib2Ebook.ExternalServices.LibSocial.Getters; @@ -15,123 +13,13 @@ public class RanobeLibGetter(BookGetterConfig config) : NewLibSocialGetterBase(c protected override int SiteId => 3; - private static readonly Dictionary RecursiveTag = new() - { - { - "paragraph", "p" - }, - { - "orderedList", "ol" - }, - { - "listItem", "li" - }, - { - "blockquote", "blockquote" - }, - }; - - private static readonly Dictionary InlineTag = new() - { - { - "horizontalRule", "
" - }, - { - "hardBreak", "
" - }, - }; - - private static readonly Dictionary MarkTag = new() - { - { - "italic", "i" - }, - { - "bold", "b" - }, - { - "underline", "u" - }, - }; - - private StringBuilder AsHtml(SocialLibBookChapter chapterResponse, IEnumerable contents) - { - var sb = new StringBuilder(); - - foreach (var content in contents) - { - if (RecursiveTag.TryGetValue(content.Type, out var tag)) - { - sb.Append(AsHtml(chapterResponse, content.Content).ToString().CoverTag(tag)); - continue; - } - - if (InlineTag.TryGetValue(content.Type, out tag)) - { - sb.Append(tag); - continue; - } - - switch (content.Type) - { - case "text": - { - var text = content.Text.HtmlEncode(); - - foreach (var mark in content.Marks) - { - if (MarkTag.TryGetValue(mark.Type, out tag)) - { - text = text.CoverTag(tag); - } - else - { - Config.Logger.LogInformation($"Неизвестый тип форматирования {mark.Type}"); - } - } - - sb.Append(text); - continue; - } - case "image": - { - if (content.Attrs.TryGetValue("images", out var images)) - { - foreach (var image in images.Deserialize[]>()) - { - if (!image.TryGetValue("image", out var imageId)) - { - continue; - } - - var attachment = chapterResponse.Attachments.FirstOrDefault(a => a.Name == imageId); - if (attachment == null) - { - continue; - } - - sb.Append($""); - } - } - - continue; - } - default: - Config.Logger.LogInformation($"Неизвестый тип {content.Type}"); - break; - } - } - - return sb; - } - protected override HtmlDocument ResponseToHtmlDoc(SocialLibBookChapter chapterResponse) { return chapterResponse.Content switch { JsonValue e => e.GetValue().AsHtmlDoc(), - JsonObject o => AsHtml(chapterResponse, o.Deserialize().Content).AsHtmlDoc(), - _ => throw new Exception("Неизвестный тип") + JsonObject o => AsHtml(chapterResponse.Attachments, o.Deserialize().Content).AsHtmlDoc(), + _ => throw new Exception("Неизвестный тип"), }; } } diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/RanobeLibBookDetail.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/RanobeLibBookDetail.cs index 431364d6..0f0bf424 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/RanobeLibBookDetail.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/RanobeLibBookDetail.cs @@ -1,3 +1,4 @@ +using System.Text.Json.Nodes; using System.Text.Json.Serialization; namespace Elib2Ebook.ExternalServices.LibSocial.Types.SocialLib; @@ -34,7 +35,7 @@ public RLBDData(int? id) public List Authors { get; set; } [JsonPropertyName("summary")] - public string Summary { get; set; } + public JsonNode Summary { get; set; } } internal class RLBDCover diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibBookChapters.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibBookChapters.cs index baffe090..7c452ec8 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibBookChapters.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.LibSocial/Types/SocialLib/SocialLibBookChapters.cs @@ -39,7 +39,7 @@ public class SocialLibBookChapter public string Number { get; set; } [JsonPropertyName("branches")] - public List Branches { get; set; } + public List Branches { get; set; } = []; [JsonPropertyName("attachments")] public SocialLibChapterAttachment[] Attachments { get; set; } = []; @@ -69,7 +69,7 @@ public string Name } } -internal class SocialLibChapterContent +public class SocialLibChapterContent { [JsonPropertyName("type")] public string Type { get; set; } @@ -87,7 +87,7 @@ internal class SocialLibChapterContent public Dictionary Attrs { get; set; } = new(); } -internal class SocialLibChapterMark +public class SocialLibChapterMark { [JsonPropertyName("type")] public string Type { get; set; } diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/LitmarketGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/LitmarketGetter.cs index 9ef94910..2bc4b961 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/LitmarketGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litmarket/LitmarketGetter.cs @@ -127,7 +127,7 @@ public override async Task Authorize() private Task GetCover(HtmlDocument doc, Uri uri) { var imagePath = doc.QuerySelector("img.img.bookCardSkeleton")?.Attributes["src"]?.Value; - return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath.AsUri().AbsolutePath)) : Task.FromResult(default(TempFile)); + return !string.IsNullOrWhiteSpace(imagePath) ? SaveImage(uri.MakeRelativeUri(imagePath)) : Task.FromResult(default(TempFile)); } private List GetToc(Response response, string title) From fffef47fc48e3f2292dae1a9dfb53c2da252789b Mon Sep 17 00:00:00 2001 From: OnlyFart Date: Thu, 9 Jul 2026 18:59:02 +0300 Subject: [PATCH 12/29] fix litres.ru --- .../LitresGetter.cs | 38 +++++++-------- .../Types/LitresPayload.cs | 46 ------------------- .../Types/Requests/LitresAuthData.cs | 2 +- .../Types/Requests/LitresAuthRequest.cs | 11 ----- .../Types/Requests/LitresBrowseArtsData.cs | 15 ------ .../Types/Requests/LitresBrowseArtsRequest.cs | 12 ----- .../Types/Requests/LitresRequestBase.cs | 15 ------ .../Types/Response/LitresAuthResponseData.cs | 6 --- .../Types/Response/LitresStaticResponse.cs | 3 ++ 9 files changed, 21 insertions(+), 127 deletions(-) delete mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/LitresPayload.cs delete mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresAuthRequest.cs delete mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresBrowseArtsData.cs delete mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresBrowseArtsRequest.cs delete mode 100644 src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresRequestBase.cs diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/LitresGetter.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/LitresGetter.cs index 6865894b..ef845b5c 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/LitresGetter.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/LitresGetter.cs @@ -35,6 +35,12 @@ private static long GetCurrentMilli() return (long)javaSpan.TotalMilliseconds / 1000; } + public override async Task Init() + { + await base.Init(); + Config.Client.DefaultRequestHeaders.Add("app-id", "1"); + } + private Uri GetFullUri(LitresArt art, string path, LitresFile file, string host = null) { var ts = GetCurrentMilli(); @@ -105,16 +111,22 @@ public override async Task Authorize() } } - var payload = LitresPayload.Create(DateTime.Now, string.Empty, SECRET_KEY, APP); - payload.Requests.Add(new LitresAuthRequest(Config.Options.Login, Config.Options.Password)); + var payload = new LitresAuthData + { + Login = Config.Options.Login, + Password = Config.Options.Password, + }; - _authData = await GetResponse(payload); + var response = await Config.Client.PostWithTriesAsync("https://api.litres.ru/foundation/api/auth/login".AsUri(), JsonContent.Create(payload)); + var responseData = await response.Content.ReadFromJsonAsync>(); - if (!_authData.Success) + if (!string.IsNullOrWhiteSpace(responseData.Error)) { - throw new Exception($"Не удалось авторизоваться. {_authData.ErrorMessage}"); + throw new Exception($"Не удалось авторизоваться. {responseData.Error}"); } + _authData = responseData.Payload.Data; + Config.Client.DefaultRequestHeaders.Add("Session-Id", _authData.Sid); var meResponse = await Config.Client.GetFromJsonAsync>("https://api.litres.ru/foundation/api/users/me/detailed"); if (meResponse?.Payload?.Data == null) @@ -126,12 +138,6 @@ public override async Task Authorize() await File.WriteAllTextAsync(saveCreds, JsonSerializer.Serialize(_authData)); } - private async Task GetResponse(LitresPayload payload) - { - var resp = await Config.Client.PostWithTriesAsync("https://catalit.litres.ru/catalitv2".AsUri(), CreatePayload(payload)); - return await resp.Content.ReadAsStringAsync().ContinueWith(t => t.Result.Deserialize>().Data); - } - private async Task GetResponse(Uri url) { var resp = await Config.Client.GetWithTriesAsync(url); @@ -143,16 +149,6 @@ private async Task GetResponse(Uri url) return await resp.Content.ReadAsStringAsync().ContinueWith(t => t.Result.Deserialize>().Payload.Data); } - private static FormUrlEncodedContent CreatePayload(LitresPayload payload) - { - var d = new Dictionary - { - ["jdata"] = JsonSerializer.Serialize(payload) - }; - - return new FormUrlEncodedContent(d); - } - public override async Task Get(Uri url) { var bookId = GetBookId(url); diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/LitresPayload.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/LitresPayload.cs deleted file mode 100644 index 71f06068..00000000 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/LitresPayload.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Security.Cryptography; -using System.Text; -using System.Text.Json.Serialization; - -namespace Elib2Ebook.ExternalServices.Litres.Types; - -internal class LitresPayload -{ - [JsonPropertyName("sha")] - public string Sha { get; set; } - - [JsonPropertyName("sid")] - public string Sid { get; set; } - - [JsonPropertyName("uilang")] - public string UiLang { get; set; } = "rus"; - - [JsonPropertyName("time")] - public string Time { get; set; } - - [JsonPropertyName("mobile_app")] - public string App { get; set; } - - [JsonPropertyName("requests")] - public List Requests { get; set; } = []; - - public static LitresPayload Create(DateTime date, string sid, string secret, string app) - { - var result = new LitresPayload - { - Time = date.ToString("yyyy-MM-ddTHH:mm:sszzzz"), App = app, Sid = sid - }; - - result.Sha = Sha256(result.Time + secret); - - return result; - } - - private static string Sha256(string str) - { - var crypt = SHA256.Create(); - var crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(str), 0, Encoding.UTF8.GetByteCount(str)); - - return crypto.Aggregate(new StringBuilder(), (sb, b) => sb.Append(b.ToString("x2"))).ToString(); - } -} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresAuthData.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresAuthData.cs index d192f1af..a3be4744 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresAuthData.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresAuthData.cs @@ -7,6 +7,6 @@ internal class LitresAuthData [JsonPropertyName("login")] public string Login { get; set; } - [JsonPropertyName("pwd")] + [JsonPropertyName("password")] public string Password { get; set; } } diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresAuthRequest.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresAuthRequest.cs deleted file mode 100644 index 51712a1a..00000000 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresAuthRequest.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Elib2Ebook.ExternalServices.Litres.Types.Requests; - -internal class LitresAuthRequest : LitresRequestBase -{ - public LitresAuthRequest(string login, string password) - { - Func = "w_create_sid"; - Param.Login = login; - Param.Password = password; - } -} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresBrowseArtsData.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresBrowseArtsData.cs deleted file mode 100644 index 8fa155e5..00000000 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresBrowseArtsData.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Elib2Ebook.ExternalServices.Litres.Types.Requests; - -internal class LitresBrowseArtsData -{ - [JsonPropertyName("currency")] - public string Currency { get; set; } - - [JsonPropertyName("anno")] - public string Anno { get; set; } - - [JsonPropertyName("id")] - public string[] Id { get; set; } -} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresBrowseArtsRequest.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresBrowseArtsRequest.cs deleted file mode 100644 index e0c42dfe..00000000 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresBrowseArtsRequest.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Elib2Ebook.ExternalServices.Litres.Types.Requests; - -internal class LitresBrowseArtsRequest : LitresRequestBase -{ - public LitresBrowseArtsRequest(string[] id) - { - Func = "r_browse_arts"; - Param.Currency = "RUB"; - Param.Anno = "1"; - Param.Id = id; - } -} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresRequestBase.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresRequestBase.cs deleted file mode 100644 index 0a618bbc..00000000 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Requests/LitresRequestBase.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Elib2Ebook.ExternalServices.Litres.Types.Requests; - -public abstract class LitresRequestBase where T : new() -{ - [JsonPropertyName("id")] - public string Id { get; set; } = "data"; - - [JsonPropertyName("func")] - public string Func { get; set; } - - [JsonPropertyName("param")] - public T Param { get; } = new(); -} diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresAuthResponseData.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresAuthResponseData.cs index a837e177..3e9071ed 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresAuthResponseData.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresAuthResponseData.cs @@ -4,12 +4,6 @@ namespace Elib2Ebook.ExternalServices.Litres.Types.Response; internal class LitresAuthResponseData { - [JsonPropertyName("success")] - public bool Success { get; set; } - [JsonPropertyName("sid")] public string Sid { get; set; } - - [JsonPropertyName("error_message")] - public string ErrorMessage { get; set; } } diff --git a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresStaticResponse.cs b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresStaticResponse.cs index 7397ccdb..0897df02 100644 --- a/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresStaticResponse.cs +++ b/src/ExternalServices/Elib2Ebook.ExternalServices.Litres/Types/Response/LitresStaticResponse.cs @@ -4,6 +4,9 @@ namespace Elib2Ebook.ExternalServices.Litres.Types.Response; internal class LitresStaticResponse { + [JsonPropertyName("errors")] + public string Error { get; set; } + [JsonPropertyName("payload")] public LitresResponse Payload { get; set; } } From 345cb2edc5e731c171d44311b2666b371a6f4a73 Mon Sep 17 00:00:00 2001 From: OnlyFart Date: Thu, 9 Jul 2026 20:54:22 +0300 Subject: [PATCH 13/29] version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 3caa5012..6531b0b8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,6 +4,6 @@ disable enable latestmajor - 2.8.2 + 2.8.3 From c71abd878ef18805c777672366b14d0742b0518a Mon Sep 17 00:00:00 2001 From: OnlyFart Date: Thu, 9 Jul 2026 21:02:32 +0300 Subject: [PATCH 14/29] fix docker --- src/Elib2EbookCli/Dockerfile | 6 +++--- src/Elib2EbookWeb/Dockerfile | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Elib2EbookCli/Dockerfile b/src/Elib2EbookCli/Dockerfile index 73d5ce32..3d5d2811 100644 --- a/src/Elib2EbookCli/Dockerfile +++ b/src/Elib2EbookCli/Dockerfile @@ -2,10 +2,10 @@ WORKDIR /app COPY . . -RUN dotnet restore Elib2EbookCli/Elib2EbookCli.csproj -RUN dotnet publish Elib2EbookCli/Elib2EbookCli.csproj -c Release -o /app/publish +RUN dotnet restore src/Elib2EbookCli/Elib2EbookCli.csproj +RUN dotnet publish src/Elib2EbookCli/Elib2EbookCli.csproj -c Release -o /app/publish FROM mcr.microsoft.com/dotnet/aspnet:9.0 WORKDIR /app COPY --from=build-env /app/publish . -ENTRYPOINT ["dotnet", "Elib2EbookCli.dll"] \ No newline at end of file +ENTRYPOINT ["dotnet", "Elib2EbookCli.dll"] diff --git a/src/Elib2EbookWeb/Dockerfile b/src/Elib2EbookWeb/Dockerfile index 48bcc99b..a5c65fdd 100644 --- a/src/Elib2EbookWeb/Dockerfile +++ b/src/Elib2EbookWeb/Dockerfile @@ -4,10 +4,10 @@ EXPOSE 8080 EXPOSE 8081 COPY . . -RUN dotnet restore Elib2EbookWeb/Elib2EbookWeb.csproj -RUN dotnet publish Elib2EbookWeb/Elib2EbookWeb.csproj -c Release -o /app/publish /p:UseAppHost=false +RUN dotnet restore src/Elib2EbookWeb/Elib2EbookWeb.csproj +RUN dotnet publish src/Elib2EbookWeb/Elib2EbookWeb.csproj -c Release -o /app/publish /p:UseAppHost=false FROM mcr.microsoft.com/dotnet/aspnet:9.0 WORKDIR /app COPY --from=build-env /app/publish . -ENTRYPOINT ["dotnet", "Elib2EbookWeb.dll"] \ No newline at end of file +ENTRYPOINT ["dotnet", "Elib2EbookWeb.dll"] From c6c283e4faf24e95b0fcb7d6d82821c8d4dc3f8b Mon Sep 17 00:00:00 2001 From: olekoloskov Date: Thu, 9 Jul 2026 21:07:17 +0300 Subject: [PATCH 15/29] fix deprecated --- .github/workflows/dotnet.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index e9190772..79e880c1 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -130,27 +130,25 @@ jobs: steps: - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4.2.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4.2.0 - name: check repository uses: actions/checkout@v5 - name: login to docker registry - uses: docker/login-action@v3 + uses: docker/login-action@v4.4.0 with: username: ${{secrets.DOCKERHUB_USERNAME}} password: ${{secrets.DOCKERHUB_TOKEN}} - id: string - uses: ASzc/change-string-case-action@v6 - with: - string: ${{ matrix.folder }} + run: echo "lowercase=${matrix.folder,,}" >> $GITHUB_OUTPUT - name: build and push docker image to registry - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7.3.0 with: platforms: linux/amd64, linux/arm64 file: "src/${{ matrix.folder }}/Dockerfile" @@ -169,7 +167,7 @@ jobs: uses: actions/checkout@v5 - name: create release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3.0.1 id: create_release with: draft: false @@ -202,7 +200,7 @@ jobs: working-directory: ${{ github.workspace }}/${{ matrix.os }}-${{ matrix.version }} - name: upload ${{ matrix.os }}-${{ matrix.version }} asset - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3.0.1 with: tag_name: ${{ needs.create_release.outputs.TAG_NAME }} files: Elib2Ebook-${{ matrix.os }}-${{ matrix.version }}.zip From 6465ed67cca758ba6b05f791261f23435b10abf5 Mon Sep 17 00:00:00 2001 From: olekoloskov Date: Thu, 9 Jul 2026 23:36:41 +0300 Subject: [PATCH 16/29] web --- Directory.Packages.props | 4 +- src/Elib2EbookWeb/Components/App.razor | 30 +- src/Elib2EbookWeb/Components/BookForm.razor | 336 +++-- .../Components/Layout/MainLayout.razor | 136 +- .../Components/Layout/MainLayout.razor.css | 188 ++- src/Elib2EbookWeb/Components/PWA.razor | 32 - .../Components/Pages/Error.razor | 39 +- .../Components/Pages/Features.razor | 91 ++ src/Elib2EbookWeb/Components/Pages/Home.razor | 17 +- .../Components/Pages/Supported.razor | 137 ++ src/Elib2EbookWeb/Components/ShortBook.razor | 77 +- src/Elib2EbookWeb/Elib2EbookWeb.csproj | 1 - src/Elib2EbookWeb/wwwroot/app.css | 1308 ++++++++++++++++- .../wwwroot/bootstrap/bootstrap.min.css | 7 - .../wwwroot/bootstrap/bootstrap.min.css.map | 1 - src/Elib2EbookWeb/wwwroot/manifest.json | 32 +- src/Elib2EbookWeb/wwwroot/sw.js | 101 ++ 17 files changed, 2207 insertions(+), 330 deletions(-) delete mode 100644 src/Elib2EbookWeb/Components/PWA.razor create mode 100644 src/Elib2EbookWeb/Components/Pages/Features.razor create mode 100644 src/Elib2EbookWeb/Components/Pages/Supported.razor delete mode 100644 src/Elib2EbookWeb/wwwroot/bootstrap/bootstrap.min.css delete mode 100644 src/Elib2EbookWeb/wwwroot/bootstrap/bootstrap.min.css.map create mode 100644 src/Elib2EbookWeb/wwwroot/sw.js diff --git a/Directory.Packages.props b/Directory.Packages.props index 90107a0c..a5056fbf 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -20,8 +20,8 @@ - + - + \ No newline at end of file diff --git a/src/Elib2EbookWeb/Components/App.razor b/src/Elib2EbookWeb/Components/App.razor index ddbe6a53..a8a5df21 100644 --- a/src/Elib2EbookWeb/Components/App.razor +++ b/src/Elib2EbookWeb/Components/App.razor @@ -1,16 +1,24 @@  - + - + - - + + + + + + + + + + @@ -35,6 +43,20 @@ } + + diff --git a/src/Elib2EbookWeb/Components/BookForm.razor b/src/Elib2EbookWeb/Components/BookForm.razor index 7e2ea421..bcbc1f7b 100644 --- a/src/Elib2EbookWeb/Components/BookForm.razor +++ b/src/Elib2EbookWeb/Components/BookForm.razor @@ -4,145 +4,299 @@ @inject IJSRuntime JsRuntime @inject ISnackbar Snackbar - - - - - - - - - - - - - - - - +
+ +
+ +
+ +
+
+
+ +
+ +
+ + + + +
+
+ + +
+
+ + + + Авторизация +
+
+
+ + + + +
+
+ + + + + +
+
+
+ + +
+ +
+ + +
@if (_startEndCSwitcher) { -
- - +
+
+ +
+
+
+ +
+
} else { -
- - +
+
+ +
+
+
+ +
+
} +
- - По номеру - По названию - - - - - - @if (_onGenerate) - { - - @if (_onGenerate) - { - @if (!string.IsNullOrWhiteSpace(_log)) - { - @_log - } - else - { - - } - } - - } - else - { - - - Скачать - - - Скачать без картинок - - - } - + +
+ @if (_onGenerate) + { +
+
+ @if (!string.IsNullOrWhiteSpace(_log)) + { + @_log + } + else + { +
+ } +
+
+ } + else + { +
+ +
+ + @if (_showMenu) + { +
+ +
+ } +
+
+ } +
+
@code { private readonly Options _options = new(); + private readonly List _formats = []; private ActionLogger _logger; private bool _onGenerate; private string _log; private bool _startEndCSwitcher; - private bool _isShow; - private InputType _passwordInput = InputType.Password; - private string _passwordInputIcon = Icons.Material.Filled.VisibilityOff; + private bool _showMenu; + private string _url; + private int? _startNum; + private int? _endNum; [Parameter] public required Func OnClick { get; set; } - void ShowPassword() + private string GetChipClass(string format) + => _formats.Contains(format) ? "format-card-selected" : ""; + + private string GetPasswordType() + => _isShow ? "text" : "password"; + + private string GetRadioClass(bool switcher) + => switcher == _startEndCSwitcher ? "radio-selected" : ""; + + private string GetCircleClass(bool switcher) + => switcher == _startEndCSwitcher ? "circle-selected" : ""; + + private void ToggleFormat(string format) { - if (_isShow) - { - _isShow = false; - _passwordInputIcon = Icons.Material.Filled.VisibilityOff; - _passwordInput = InputType.Password; - } + if (_formats.Contains(format)) + _formats.Remove(format); else - { - _isShow = true; - _passwordInputIcon = Icons.Material.Filled.Visibility; - _passwordInput = InputType.Text; - } + _formats.Add(format); + } + + private void ShowPassword() + { + _isShow = !_isShow; + } + + private void ToggleMenu() + { + _showMenu = !_showMenu; } private async Task WithImages() { + _showMenu = false; _options.NoImage = false; await Download(); } private async Task NoImages() { + _showMenu = false; _options.NoImage = true; await Download(); } private async Task Download() { - if (_options.Url == null || !_options.Url.Any() || string.IsNullOrWhiteSpace(_options.Url.First())) + if (string.IsNullOrWhiteSpace(_url)) { Snackbar.Add("Укажите ссылку на книгу", Severity.Error); return; } - if (_options.Format == null || !_options.Format.Any()) + if (_formats.Count == 0) { Snackbar.Add("Выберите формат", Severity.Error); return; } + _options.Format = _formats; + _options.Url = new[] { _url }; + + if (_startNum.HasValue) + _options.Start = _startNum; + if (_endNum.HasValue) + _options.End = _endNum; + _onGenerate = true; + _showMenu = false; _logger = new ActionLogger(str => { _log = str.Crop(300); @@ -153,14 +307,14 @@ _onGenerate = false; } - private void OnSwitch(bool obj) + private void OnSwitch(bool value) { - _startEndCSwitcher = obj; - + _startEndCSwitcher = value; _options.Start = null; _options.End = null; _options.StartName = null; _options.EndName = null; + _startNum = null; + _endNum = null; } - } diff --git a/src/Elib2EbookWeb/Components/Layout/MainLayout.razor b/src/Elib2EbookWeb/Components/Layout/MainLayout.razor index 92d43a9d..c038720e 100644 --- a/src/Elib2EbookWeb/Components/Layout/MainLayout.razor +++ b/src/Elib2EbookWeb/Components/Layout/MainLayout.razor @@ -1,29 +1,135 @@ @inherits LayoutComponentBase +@using Microsoft.AspNetCore.Components.Routing - + +
+
+
+
+ + + + + Elib2Ebook +
+ + +
+
-
-
- - Elib2Ebook - - - - - + @if (_mobileNavOpen) + { +
+ } -
+
+
@Body -
+
+ +
+ +
- An unhandled error has occurred. - Reload + Произошла ошибка. + Перезагрузить 🗙
+ +@code { + private bool _mobileNavOpen; + + private MudTheme _theme = new() + { + PaletteLight = new PaletteLight + { + Primary = "#1a73e8", + Secondary = "#5f6368", + Tertiary = "#34a853", + Surface = "#ffffff", + Background = "#ffffff", + AppbarBackground = "#ffffff", + DrawerBackground = "#ffffff", + DrawerText = "#1f1f1f", + AppbarText = "#1f1f1f", + TextPrimary = "#1f1f1f", + TextSecondary = "#5f6368", + ActionDefault = "#5f6368", + ActionDisabled = "#c4c7c5", + Divider = "#e8eaed", + Error = "#d93025", + Info = "#1a73e8", + Success = "#34a853", + Warning = "#fbbc04", + TableLines = "#e8eaed", + LinesDefault = "#dadce0", + LinesInputs = "#dadce0", + Dark = "#1f1f1f", + DarkDarken = "#000000", + } + }; + + private void ToggleMobileNav() + { + _mobileNavOpen = !_mobileNavOpen; + } + + private void CloseMobileNav() + { + _mobileNavOpen = false; + } +} diff --git a/src/Elib2EbookWeb/Components/Layout/MainLayout.razor.css b/src/Elib2EbookWeb/Components/Layout/MainLayout.razor.css index d49e4b7e..db288fdf 100644 --- a/src/Elib2EbookWeb/Components/Layout/MainLayout.razor.css +++ b/src/Elib2EbookWeb/Components/Layout/MainLayout.razor.css @@ -1,96 +1,160 @@ -.page { - position: relative; +/* Google-style app layout */ +.app-layout { display: flex; flex-direction: column; + min-height: 100vh; } -main { - flex: 1; +.app-header { + position: sticky; + top: 0; + z-index: 100; + background: var(--md-sys-color-surface); + border-bottom: 1px solid var(--md-sys-color-outline); + box-shadow: var(--md-ref-elevation-1); +} + +.app-header-content { + max-width: 840px; + margin: 0 auto; + display: flex; + align-items: center; + justify-content: space-between; + height: 56px; + padding: 0 16px; } -.sidebar { - background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); +.app-header-left { + display: flex; + align-items: center; + gap: 12px; } -.top-row { - background-color: #f7f7f7; - border-bottom: 1px solid #d6d5d5; - justify-content: flex-end; - height: 3.5rem; +.app-header-right { display: flex; align-items: center; } -.top-row ::deep a, .top-row ::deep .btn-link { - white-space: nowrap; - margin-left: 1.5rem; +.app-logo-icon { + width: 28px; + height: 28px; + color: var(--md-sys-color-primary); +} + +.app-title { + font-size: 1.25rem !important; + font-weight: 500 !important; + color: var(--md-sys-color-on-surface); + letter-spacing: -0.01em; text-decoration: none; } -.top-row ::deep a:hover, .top-row ::deep .btn-link:hover { - text-decoration: underline; +.github-btn { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + transition: background 0.15s; } -.top-row ::deep a:first-child { - overflow: hidden; - text-overflow: ellipsis; +.github-btn:hover { + background: var(--md-sys-color-surface-variant) !important; } -@media (max-width: 640.98px) { - .top-row { - justify-content: space-between; - } +.app-main { + flex: 1; + display: flex; + justify-content: center; +} - .top-row ::deep a, .top-row ::deep .btn-link { - margin-left: 0; - } +.app-content { + width: 100%; + max-width: 840px; + padding: 24px 16px 48px; } -@media (min-width: 641px) { - .page { - flex-direction: row; - } +/* Hamburger button (hidden on desktop) */ +.hamburger-btn { + display: none; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border: none; + border-radius: 50%; + background: transparent; + color: var(--md-sys-color-on-surface-variant); + cursor: pointer; + padding: 0; + transition: background 0.15s; + flex-shrink: 0; +} + +.hamburger-btn:hover { + background: var(--md-sys-color-surface-variant); +} + +/* Mobile overlay */ +.mobile-overlay { + display: none; +} - .sidebar { - width: 250px; - height: 100vh; - position: sticky; - top: 0; +/* ====== MOBILE BREAKPOINT ====== */ + +@media (max-width: 640px) { + .hamburger-btn { + display: flex; } - .top-row { - position: sticky; - top: 0; - z-index: 1; + .app-nav { + position: fixed; + top: 56px; + left: -280px; + width: 280px; + height: calc(100vh - 56px); + flex-direction: column; + gap: 4px; + margin-left: 0; + padding: 8px; + background: var(--md-sys-color-surface); + border-right: 1px solid var(--md-sys-color-outline); + box-shadow: var(--md-ref-elevation-3); + z-index: 200; + transition: left 0.25s ease; + overflow-y: auto; } - .top-row.auth ::deep a:first-child { - flex: 1; - text-align: right; - width: 0; + .app-nav.app-nav-open { + left: 0; } - .top-row, article { - padding-left: 2rem !important; - padding-right: 1.5rem !important; + .app-nav-link { + padding: 12px 16px; + gap: 12px; + width: 100%; + border-radius: var(--md-sys-shape-corner-small); + font-size: var(--md-sys-typescale-body-large-size); } -} -#blazor-error-ui { - background: lightyellow; - bottom: 0; - box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); - display: none; - left: 0; - padding: 0.6rem 1.25rem 0.7rem 1.25rem; - position: fixed; - width: 100%; - z-index: 1000; + .mobile-overlay { + display: block; + position: fixed; + top: 56px; + left: 0; + width: 100%; + height: calc(100vh - 56px); + background: rgba(0, 0, 0, 0.3); + z-index: 150; + } } -#blazor-error-ui .dismiss { - cursor: pointer; - position: absolute; - right: 0.75rem; - top: 0.5rem; +@media (min-width: 641px) { + .app-nav { + display: flex; + align-items: center; + gap: 4px; + margin-left: 24px; + } } diff --git a/src/Elib2EbookWeb/Components/PWA.razor b/src/Elib2EbookWeb/Components/PWA.razor deleted file mode 100644 index d971a9d0..00000000 --- a/src/Elib2EbookWeb/Components/PWA.razor +++ /dev/null @@ -1,32 +0,0 @@ -Установить приложение - - - diff --git a/src/Elib2EbookWeb/Components/Pages/Error.razor b/src/Elib2EbookWeb/Components/Pages/Error.razor index fd81a455..e410323b 100644 --- a/src/Elib2EbookWeb/Components/Pages/Error.razor +++ b/src/Elib2EbookWeb/Components/Pages/Error.razor @@ -1,30 +1,21 @@ @page "/Error" @using System.Diagnostics -Error - -

Error.

-

An error occurred while processing your request.

- -@if (ShowRequestId) -{ -

- Request ID: @RequestId -

-} - -

Development Mode

-

- Swapping to Development environment will display more detailed information about the error that - occurred. -

-

- The Development environment shouldn't be enabled for deployed applications. - It can result in displaying sensitive information from exceptions to end users. - For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT - environment variable to Development - and restarting the app. -

+Ошибка + +
+
+

Произошла ошибка

+

При обработке вашего запроса возникла непредвиденная ошибка.

+ + @if (ShowRequestId) + { +

+ ID запроса: @RequestId +

+ } +
+
@code{ diff --git a/src/Elib2EbookWeb/Components/Pages/Features.razor b/src/Elib2EbookWeb/Components/Pages/Features.razor new file mode 100644 index 00000000..d77d5231 --- /dev/null +++ b/src/Elib2EbookWeb/Components/Pages/Features.razor @@ -0,0 +1,91 @@ +@page "/features" +@rendermode InteractiveServer + +Возможности — Elib2Ebook + +
+
+

Возможности

+

Всё, что нужно для комфортного чтения в любом формате

+
+ +
+
+
+ + + +
+

Множество форматов

+

+ EPUB, FB2, CBZ, TXT — скачивайте книги в удобном формате для любого устройства. + Поддержка всех популярных форматов электронных книг. +

+
+ +
+
+ + + +
+

Авторизация

+

+ Поддержка авторизации на сайтах с платным контентом. + Введите логин и пароль — всё остальное сделаем мы. +

+
+ +
+
+ + + +
+

Выбор глав

+

+ Скачивайте выборочные главы по номерам или названиям. + Не нужно ждать, пока обработается вся книга целиком. +

+
+ +
+
+ + + +
+

Изображения

+

+ Поддержка изображений в книгах. Можно скачать с картинками + или без — выбор остаётся за вами. +

+
+ +
+
+ + + +
+

Бесплатно

+

+ Полностью бесплатный open-source инструмент. + Никаких ограничений, подписок и скрытых платежей. +

+
+ +
+
+ + + +
+

Настраиваемо

+

+ Гибкие настройки: выбор формата, диапазона глав, + авторизации и многое другое под ваши задачи. +

+
+
+
diff --git a/src/Elib2EbookWeb/Components/Pages/Home.razor b/src/Elib2EbookWeb/Components/Pages/Home.razor index a25f9228..3da8199f 100644 --- a/src/Elib2EbookWeb/Components/Pages/Home.razor +++ b/src/Elib2EbookWeb/Components/Pages/Home.razor @@ -10,22 +10,17 @@ Elib2Ebook - - - - - +
+ +
@if (_shortBook != null) { - - - - - +
+ +
} - @code { private Book _shortBook; diff --git a/src/Elib2EbookWeb/Components/Pages/Supported.razor b/src/Elib2EbookWeb/Components/Pages/Supported.razor new file mode 100644 index 00000000..593bbfe1 --- /dev/null +++ b/src/Elib2EbookWeb/Components/Pages/Supported.razor @@ -0,0 +1,137 @@ +@page "/supported" +@rendermode InteractiveServer + +Поддерживаемые сайты — Elib2Ebook + +
+
+

Поддерживаемые сайты

+

Более 50 книжных сайтов, с которых можно скачивать книги

+
+ + + +
+ @foreach (var site in FilteredSites) + { +
+
+ @switch (site.Icon) + { + case "book": + + + + break; + case "globe": + + + + break; + case "manga": + + + + break; + } +
+
+ @site.Name + @site.Url +
+
+ } + @if (FilteredSites.Count == 0) + { +
+ + + +

Ничего не найдено

+
+ } +
+
+ +@code { + private string _search = string.Empty; + + private IReadOnlyList FilteredSites => + string.IsNullOrWhiteSpace(_search) + ? Sites + : Sites.Where(s => s.Name.Contains(_search, StringComparison.OrdinalIgnoreCase) || + s.Url.Contains(_search, StringComparison.OrdinalIgnoreCase)).ToList(); + + private static readonly IReadOnlyList Sites = new List + { + new("Author.Today", "https://author.today", "globe"), + new("Book in Book", "https://bookinbook.com", "book"), + new("Bookinist", "https://bookinist.org", "book"), + new("BookRiver", "https://bookriver.ru", "book"), + new("Bookstab", "https://bookstab.ru", "book"), + new("BooksTime", "https://bookstime.ru", "book"), + new("Boosty", "https://boosty.to", "globe"), + new("Boovel", "https://boovel.ru", "book"), + new("DarkNovels", "https://darknovels.ru", "book"), + new("Desu", "https://desu.me", "manga"), + new("Dreame", "https://dreame.com", "book"), + new("FanFicus", "https://fanficus.com", "book"), + new("Fb2Top", "https://fb2.top", "book"), + new("Ficbook", "https://ficbook.net", "book"), + new("FictionBook", "https://fictionbook.ru", "book"), + new("Freedlit", "https://freedlit.space", "book"), + new("Freedom", "https://freedom.tm", "book"), + new("HogwartsNet", "https://hogwartsnet.ru", "book"), + new("HotNovelPub", "https://hotnovelpub.com", "book"), + new("HubBook", "https://hubbook.ru", "book"), + new("iGram", "https://igram.net", "globe"), + new("Jaomix", "https://jaomix.ru", "book"), + new("Libbox", "https://libbox.ru", "book"), + new("LibKing", "https://libking.ru", "book"), + new("Librebook", "https://librebook.me", "book"), + new("LibSocial", "https://lib.social", "book"), + new("Libst", "https://libst.ru", "book"), + new("Litgorod", "https://litgorod.ru", "book"), + new("LitLife", "https://litlife.club", "book"), + new("Litmarket", "https://litmarket.ru", "book"), + new("Litmir", "https://litmir.club", "book"), + new("Litnet", "https://litnet.com", "book"), + new("Litres", "https://litres.ru", "book"), + new("Litsovet", "https://litsovet.ru", "book"), + new("Manga Mammy", "https://mangamammy.ru", "manga"), + new("MirKnig", "https://mir-knig.com", "book"), + new("MyBook", "https://mybook.ru", "book"), + new("Neobook", "https://neobook.org", "book"), + new("Novelhall", "https://novelhall.com", "book"), + new("NovelTranslate", "https://noveltranslate.com", "book"), + new("Novelxo", "https://novelxo.com", "book"), + new("OnlineKnigi", "https://online-knigi.com", "book"), + new("Prodaman", "https://prodaman.ru", "book"), + new("Ranobe", "https://ranobe.me", "book"), + new("RanobeHub", "https://ranobehub.org", "book"), + new("RanobeOvh", "https://ranobe.ovh", "book"), + new("RanobesCom", "https://ranobes.com", "book"), + new("Readli", "https://readli.net", "book"), + new("ReadManga", "https://readmanga.me", "manga"), + new("Renovels", "https://renovels.org", "book"), + new("Romfant", "https://romfant.ru", "book"), + new("RoyalRoad", "https://royalroad.com", "book"), + new("Rulate", "https://tl.rulate.ru", "book"), + new("Samlib", "https://samlib.ru", "book"), + new("Stroki MTS", "https://stroki.mts.ru", "book"), + new("TopLiba", "https://topliba.com", "book"), + new("Twilight Russia", "https://twilightrussia.ru", "book"), + new("Wattpad", "https://wattpad.com", "book"), + new("WuxiaWorld", "https://wuxiaworld.com", "book"), + new("Yandex Books", "https://books.yandex.ru", "book"), + new("Younettranslate", "https://younettranslate.com", "book"), + new("Acomics", "https://acomics.ru", "manga"), + }; + + private record SiteInfo(string Name, string Url, string Icon); +} diff --git a/src/Elib2EbookWeb/Components/ShortBook.razor b/src/Elib2EbookWeb/Components/ShortBook.razor index 615f6835..ecb08c98 100644 --- a/src/Elib2EbookWeb/Components/ShortBook.razor +++ b/src/Elib2EbookWeb/Components/ShortBook.razor @@ -1,53 +1,50 @@ @using Elib2Ebook.Domain.Book - - - - - - - - - @Book.Title - +
+
+ Обложка +
+
+ + @Book.Title + - @if (Book.Author != null) + @if (Book.Author != null) + { +
+ Автор: + @if (Book.Author.Url == null) { - @if (Book.Author.Url == null) - { - Автор: @Book.Author.Name - } - else - { - Автор: - @Book.Author.Name - - } + @Book.Author.Name } - - @if (Book.Seria != null) + else { - @if (Book.Seria?.Url == null) - { - Серия: @Seria - } - else - { - Серия: - @Seria - - } + @Book.Author.Name } +
+ } - @if (!string.IsNullOrWhiteSpace(Book.Annotation)) + @if (Book.Seria != null) + { +
+ Серия: + @if (Book.Seria?.Url == null) + { + @Seria + } + else { - @((MarkupString)Book.Annotation) + @Seria } - - - - +
+ } + + @if (!string.IsNullOrWhiteSpace(Book.Annotation)) + { +
@((MarkupString)Book.Annotation)
+ } +
+
@code { diff --git a/src/Elib2EbookWeb/Elib2EbookWeb.csproj b/src/Elib2EbookWeb/Elib2EbookWeb.csproj index 33cc3c98..95436fe8 100644 --- a/src/Elib2EbookWeb/Elib2EbookWeb.csproj +++ b/src/Elib2EbookWeb/Elib2EbookWeb.csproj @@ -15,5 +15,4 @@ PreserveNewest - diff --git a/src/Elib2EbookWeb/wwwroot/app.css b/src/Elib2EbookWeb/wwwroot/app.css index 98afec1c..48030f5e 100644 --- a/src/Elib2EbookWeb/wwwroot/app.css +++ b/src/Elib2EbookWeb/wwwroot/app.css @@ -1,58 +1,1306 @@ -html, body { - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; +/* Google Material Design 3 - Light Theme */ +:root { + --md-sys-color-primary: #1a73e8; + --md-sys-color-on-primary: #ffffff; + --md-sys-color-primary-container: #d3e3fd; + --md-sys-color-on-primary-container: #041e49; + --md-sys-color-secondary: #5f6368; + --md-sys-color-on-secondary: #ffffff; + --md-sys-color-surface: #ffffff; + --md-sys-color-on-surface: #1f1f1f; + --md-sys-color-surface-variant: #f1f3f4; + --md-sys-color-on-surface-variant: #444746; + --md-sys-color-outline: #dadce0; + --md-sys-color-outline-variant: #c4c7c5; + --md-sys-color-error: #d93025; + --md-sys-color-background: #ffffff; + --md-sys-color-on-background: #1f1f1f; + --md-sys-color-shadow: rgba(0, 0, 0, 0.12); + --md-ref-elevation-1: 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 3px 1px rgba(0, 0, 0, 0.15); + --md-ref-elevation-2: 0 1px 2px rgba(0, 0, 0, 0.3), 0 2px 6px 2px rgba(0, 0, 0, 0.15); + --md-ref-elevation-3: 0 4px 8px 3px rgba(0, 0, 0, 0.15), 0 1px 3px rgba(0, 0, 0, 0.3); + --md-sys-shape-corner-extra-small: 4px; + --md-sys-shape-corner-small: 8px; + --md-sys-shape-corner-medium: 12px; + --md-sys-shape-corner-large: 16px; + --md-sys-shape-corner-extra-large: 28px; + --md-sys-typescale-headline-large-size: 2rem; + --md-sys-typescale-headline-medium-size: 1.75rem; + --md-sys-typescale-title-large-size: 1.375rem; + --md-sys-typescale-title-medium-size: 1rem; + --md-sys-typescale-body-large-size: 1rem; + --md-sys-typescale-body-medium-size: 0.875rem; + --md-sys-typescale-label-large-size: 0.875rem; + --md-sys-typescale-label-medium-size: 0.75rem; } -a, .btn-link { - color: #006bb7; +*, +*::before, +*::after { + box-sizing: border-box; } -.btn-primary { - color: #fff; - background-color: #1b6ec2; - border-color: #1861ac; +html { + font-family: 'Google Sans', 'Roboto', Arial, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } -.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { - box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; +body { + margin: 0; + padding: 0; + background-color: var(--md-sys-color-background); + color: var(--md-sys-color-on-background); + min-height: 100vh; } -.content { - padding-top: 1.1rem; +/* Google-style scrollbar */ +::-webkit-scrollbar { + width: 8px; } -h1:focus { +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background-color: var(--md-sys-color-outline); + border-radius: var(--md-sys-shape-corner-small); +} + +/* Focus visible */ +:focus-visible { + outline: 2px solid var(--md-sys-color-primary); + outline-offset: 2px; +} + +/* ====== NAVIGATION ====== */ + +.app-nav { + display: flex; + align-items: center; + gap: 4px; + margin-left: 24px; +} + +.app-nav-link { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 12px; + font-size: var(--md-sys-typescale-body-medium-size); + font-weight: 500; + color: var(--md-sys-color-on-surface-variant); + text-decoration: none; + border-radius: var(--md-sys-shape-corner-small); + transition: background 0.15s, color 0.15s; + white-space: nowrap; +} + +.app-nav-link:hover { + background: var(--md-sys-color-surface-variant); + color: var(--md-sys-color-on-surface); +} + +.app-nav-link.active { + background: var(--md-sys-color-primary-container); + color: var(--md-sys-color-on-primary-container); +} + +/* ====== FOOTER ====== */ + +.app-footer { + border-top: 1px solid var(--md-sys-color-outline); + background: var(--md-sys-color-surface); + margin-top: auto; +} + +.app-footer-content { + max-width: 840px; + margin: 0 auto; + padding: 24px 16px; + display: flex; + flex-direction: column; + gap: 16px; +} + +.app-footer-links { + display: flex; + gap: 16px; + flex-wrap: wrap; +} + +.app-footer-link { + font-size: var(--md-sys-typescale-body-medium-size); + color: var(--md-sys-color-on-surface-variant); + text-decoration: none; + transition: color 0.15s; +} + +.app-footer-link:hover { + color: var(--md-sys-color-primary); +} + +.app-footer-info { + font-size: var(--md-sys-typescale-body-medium-size); + color: var(--md-sys-color-on-surface-variant); + line-height: 1.5; +} + +.app-footer-info p { + margin: 0; +} + +.app-footer-copy { + margin-top: 4px !important; + font-size: var(--md-sys-typescale-label-medium-size); + opacity: 0.8; +} + +/* ====== FORM STYLES (Google Material Design) ====== */ + +.book-form { + display: flex; + flex-direction: column; + gap: 0; + background: var(--md-sys-color-surface); + border: 1px solid var(--md-sys-color-outline); + border-radius: var(--md-sys-shape-corner-medium); + overflow: visible; + box-shadow: var(--md-ref-elevation-1); +} + +.form-section { + padding: 16px 20px; + border-bottom: 1px solid var(--md-sys-color-outline); +} + +.form-section:last-child { + border-bottom: none; +} + +.form-actions { + padding: 12px 20px 16px; + position: relative; + z-index: 100; +} + +/* Google label */ +.google-label { + display: block; + font-size: var(--md-sys-typescale-body-medium-size); + font-weight: 500; + color: var(--md-sys-color-on-surface-variant); + margin-bottom: 8px; + line-height: 1.5; +} + +/* Google-style text field */ +.google-textfield { + position: relative; + width: 100%; +} + +.google-input { + width: 100%; + padding: 12px 12px; + font-family: 'Google Sans', 'Roboto', Arial, sans-serif; + font-size: var(--md-sys-typescale-body-large-size); + color: var(--md-sys-color-on-surface); + background: var(--md-sys-color-surface-variant); + border: none; + border-radius: var(--md-sys-shape-corner-extra-small); + outline: none; + transition: background 0.15s; +} + +.google-input:focus { + background: var(--md-sys-color-surface); + box-shadow: inset 0 0 0 2px var(--md-sys-color-primary); +} + +.google-input::placeholder { + color: var(--md-sys-color-on-surface-variant); + opacity: 0.7; +} + +.google-input-underline { + display: none; +} + +/* Auth group (login + password combined) */ +.auth-label { + display: flex; + align-items: center; + gap: 8px; + font-size: var(--md-sys-typescale-body-medium-size); + font-weight: 500; + color: var(--md-sys-color-on-surface-variant); + margin-bottom: 10px; + line-height: 1.5; +} + +.auth-group { + display: flex; + flex-direction: column; + gap: 8px; +} + +.auth-input-wrapper { + display: flex; + align-items: center; + gap: 0; + background: var(--md-sys-color-surface-variant); + border: 1px solid var(--md-sys-color-outline); + border-radius: var(--md-sys-shape-corner-small); + transition: border-color 0.2s, box-shadow 0.2s; + overflow: hidden; +} + +.auth-input-wrapper:focus-within { + border-color: var(--md-sys-color-primary); + box-shadow: inset 0 0 0 1px var(--md-sys-color-primary); +} + +.auth-icon { + flex-shrink: 0; + color: var(--md-sys-color-on-surface-variant); + margin-left: 12px; + margin-right: 4px; +} + +.auth-input-wrapper:focus-within .auth-icon { + color: var(--md-sys-color-primary); +} + +.auth-input { + flex: 1; + padding: 10px 12px 10px 4px; + font-family: 'Google Sans', 'Roboto', Arial, sans-serif; + font-size: var(--md-sys-typescale-body-large-size); + color: var(--md-sys-color-on-surface); + background: transparent; + border: none; outline: none; } -.valid.modified:not([type=checkbox]) { - outline: 1px solid #26b050; +.auth-input::placeholder { + color: var(--md-sys-color-on-surface-variant); + opacity: 0.7; +} + +.auth-password-toggle { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + margin-right: 4px; + background: none; + border: none; + border-radius: 50%; + cursor: pointer; + color: var(--md-sys-color-on-surface-variant); + padding: 0; + transition: background 0.15s; + flex-shrink: 0; +} + +.auth-password-toggle:hover { + background: rgba(95, 99, 104, 0.12); +} + +/* Format cards (square cards with icon + description) */ +.format-cards { + display: flex; + gap: 12px; + flex-wrap: wrap; + justify-content: center; } -.invalid { - outline: 1px solid #e50000; +.format-card { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + width: 140px; + padding: 20px 12px 16px; + font-family: 'Google Sans', 'Roboto', Arial, sans-serif; + color: var(--md-sys-color-on-surface); + background: var(--md-sys-color-surface); + border: 1px solid var(--md-sys-color-outline); + border-radius: var(--md-sys-shape-corner-medium); + cursor: pointer; + transition: all 0.2s; + user-select: none; } -.validation-message { - color: #e50000; +.format-card:hover { + background: var(--md-sys-color-surface-variant); + border-color: var(--md-sys-color-on-surface-variant); } -.blazor-error-boundary { - background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; - padding: 1rem 1rem 1rem 3.7rem; +.format-card-selected { + background: var(--md-sys-color-primary-container); + color: var(--md-sys-color-on-primary-container); + border-color: var(--md-sys-color-primary); + border-width: 2px; +} + +.format-card-selected:hover { + background: var(--md-sys-color-primary-container); + filter: brightness(0.95); +} + +.format-card-icon { + display: flex; + align-items: center; + justify-content: center; + width: 48px; + height: 48px; + border-radius: var(--md-sys-shape-corner-small); + background: var(--md-sys-color-surface-variant); + color: var(--md-sys-color-primary); + margin-bottom: 4px; +} + +.format-card-selected .format-card-icon { + background: var(--md-sys-color-primary); + color: var(--md-sys-color-on-primary); +} + +.format-card-label { + font-size: var(--md-sys-typescale-title-medium-size); + font-weight: 500; + line-height: 1.2; +} + +.format-card-desc { + font-size: var(--md-sys-typescale-label-medium-size); + color: var(--md-sys-color-on-surface-variant); + line-height: 1.3; + text-align: center; +} + +.format-card-selected .format-card-desc { + color: var(--md-sys-color-on-primary-container); +} + +/* Password field */ +.password-wrapper { + display: flex; + align-items: center; + gap: 0; +} + +.password-field { + flex: 1; +} + +.password-toggle { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + margin-left: 8px; + background: none; + border: none; + border-radius: 50%; + cursor: pointer; + color: var(--md-sys-color-on-surface-variant); + padding: 0; + transition: background 0.15s; + flex-shrink: 0; +} + +.password-toggle:hover { + background: var(--md-sys-color-surface-variant); +} + +/* Radio group */ +.radio-group { + display: flex; + gap: 4px; + background: var(--md-sys-color-surface-variant); + border-radius: var(--md-sys-shape-corner-small); + padding: 4px; + margin-bottom: 12px; +} + +.google-radio { + display: flex; + align-items: center; + gap: 8px; + flex: 1; + padding: 8px 12px; + font-family: 'Google Sans', 'Roboto', Arial, sans-serif; + font-size: var(--md-sys-typescale-body-medium-size); + font-weight: 500; + color: var(--md-sys-color-on-surface-variant); + background: transparent; + border: none; + border-radius: var(--md-sys-shape-corner-extra-small); + cursor: pointer; + transition: all 0.2s; + user-select: none; + justify-content: center; +} + +.google-radio:hover { + background: rgba(95, 99, 104, 0.08); +} + +.radio-selected { + background: var(--md-sys-color-surface); + color: var(--md-sys-color-on-surface); + box-shadow: var(--md-ref-elevation-1); +} + +.radio-selected:hover { + background: var(--md-sys-color-surface); +} + +.radio-circle { + width: 16px; + height: 16px; + border-radius: 50%; + border: 2px solid var(--md-sys-color-on-surface-variant); + transition: all 0.2s; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.circle-selected { + border-color: var(--md-sys-color-primary); + border-width: 5px; +} + +/* Range fields */ +.range-fields { + display: flex; + gap: 12px; +} + +.range-field { + flex: 1; +} + +/* Buttons */ +.google-button { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 10px 24px; + font-family: 'Google Sans', 'Roboto', Arial, sans-serif; + font-size: var(--md-sys-typescale-label-large-size); + font-weight: 500; + border: none; + border-radius: var(--md-sys-shape-corner-small); + cursor: pointer; + transition: box-shadow 0.2s, background 0.2s; + user-select: none; + text-decoration: none; + letter-spacing: 0.01em; + line-height: 1.25rem; + min-height: 40px; +} + +.google-button-primary { + background: var(--md-sys-color-primary); + color: var(--md-sys-color-on-primary); +} + +.google-button-primary:hover { + background: #1565c0; + box-shadow: var(--md-ref-elevation-2); +} + +.google-button-primary:active { + background: #0d47a1; + box-shadow: var(--md-ref-elevation-1); +} + +.google-button-disabled { + background: var(--md-sys-color-surface-variant); + color: var(--md-sys-color-on-surface-variant); + cursor: not-allowed; + opacity: 0.8; +} + +.full-width { + width: 100%; +} + +.button-content { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + overflow: hidden; + max-width: 100%; +} + +.button-log { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; + font-size: var(--md-sys-typescale-body-medium-size); +} + +/* Button group with split menu */ +.button-group { + display: flex; + align-items: stretch; + gap: 0; + width: 100%; +} + +.button-group .google-button-primary { + flex: 1; + border-radius: var(--md-sys-shape-corner-small) 0 0 var(--md-sys-shape-corner-small); +} + +.split-menu { + position: relative; +} + +.split-toggle { + display: flex; + align-items: center; + justify-content: center; + width: 44px; + min-height: 40px; + background: var(--md-sys-color-primary); + color: var(--md-sys-color-on-primary); + border: none; + border-left: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 0 var(--md-sys-shape-corner-small) var(--md-sys-shape-corner-small) 0; + cursor: pointer; + transition: background 0.2s; +} + +.split-toggle:hover { + background: #1565c0; +} + +.split-dropdown { + position: absolute; + bottom: 100%; + right: 0; + margin-bottom: 8px; + background: var(--md-sys-color-surface); + border: 1px solid var(--md-sys-color-outline); + border-radius: var(--md-sys-shape-corner-small); + box-shadow: var(--md-ref-elevation-2); + z-index: 200; + min-width: 200px; + overflow: hidden; +} + +.dropdown-item { + display: flex; + align-items: center; + padding: 12px 16px; + font-family: 'Google Sans', 'Roboto', Arial, sans-serif; + font-size: var(--md-sys-typescale-body-medium-size); + color: var(--md-sys-color-on-surface); + cursor: pointer; + transition: background 0.15s; + white-space: nowrap; +} + +.dropdown-item:hover { + background: var(--md-sys-color-surface-variant); +} + +/* Google spinner */ +.google-spinner { + width: 20px; + height: 20px; + border: 2.5px solid var(--md-sys-color-on-surface-variant); + border-top-color: var(--md-sys-color-primary); + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* ====== SHORT BOOK CARD ====== */ + +.book-card { + display: flex; + gap: 20px; + padding: 20px; + background: var(--md-sys-color-surface); + border: 1px solid var(--md-sys-color-outline); + border-radius: var(--md-sys-shape-corner-medium); + box-shadow: var(--md-ref-elevation-1); +} + +.book-card-cover { + width: 128px; + min-width: 128px; + height: 192px; + border-radius: var(--md-sys-shape-corner-small); + overflow: hidden; + background: var(--md-sys-color-surface-variant); +} + +.book-card-cover img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +.book-card-info { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 8px; +} + +.book-card-title { + font-size: 1.25rem; + font-weight: 500; + color: var(--md-sys-color-on-surface); + line-height: 1.4; + text-decoration: none; + display: block; +} + +.book-card-title:hover { + color: var(--md-sys-color-primary); +} + +.book-card-meta { + font-size: var(--md-sys-typescale-body-medium-size); + color: var(--md-sys-color-on-surface-variant); + line-height: 1.5; +} + +.book-card-meta a { + color: var(--md-sys-color-primary); + text-decoration: none; +} + +.book-card-meta a:hover { + text-decoration: underline; +} + +.book-card-annotation { + font-size: var(--md-sys-typescale-body-medium-size); + color: var(--md-sys-color-on-surface); + line-height: 1.6; + margin-top: 4px; + white-space: pre-line; + max-height: 160px; + overflow-y: auto; +} + +/* ====== BLAZOR ERROR UI ====== */ + +#blazor-error-ui { + background: var(--md-sys-color-error); color: white; + bottom: 0; + box-shadow: 0 -2px 8px var(--md-sys-color-shadow); + display: none; + left: 0; + padding: 0.6rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; + font-size: var(--md-sys-typescale-body-medium-size); +} + +#blazor-error-ui .reload { + color: white; + margin-left: 0.5rem; + text-decoration: underline; + cursor: pointer; +} + +#blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 50%; + transform: translateY(-50%); +} + +/* ====== HOME PAGE ====== */ + +.home-page { + display: flex; + flex-direction: column; + gap: 24px; +} + +.home-header { + text-align: center; + padding: 40px 0 8px; +} + +.home-header h1 { + font-size: 2.5rem; + font-weight: 500; + color: var(--md-sys-color-on-surface); + margin: 0 0 12px; + letter-spacing: -0.02em; + line-height: 1.2; +} + +.home-header p { + font-size: var(--md-sys-typescale-body-large-size); + color: var(--md-sys-color-on-surface-variant); + margin: 0; + line-height: 1.6; + max-width: 600px; + margin: 0 auto; +} + +.home-stats { + display: flex; + gap: 16px; + justify-content: center; + padding: 24px 0; +} + +.home-stat { + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + padding: 16px 24px; + background: var(--md-sys-color-surface-variant); + border-radius: var(--md-sys-shape-corner-medium); + min-width: 120px; +} + +.home-stat-value { + font-size: 1.75rem; + font-weight: 500; + color: var(--md-sys-color-primary); + line-height: 1.2; +} + +.home-stat-label { + font-size: var(--md-sys-typescale-body-medium-size); + color: var(--md-sys-color-on-surface-variant); + text-align: center; +} + +.home-book-result { + margin-top: 24px; +} + +/* ====== FEATURES PAGE ====== */ + +.features-page { + display: flex; + flex-direction: column; + gap: 32px; + padding: 24px 0; +} + +.features-header { + text-align: center; +} + +.features-title { + font-size: 2.25rem; + font-weight: 500; + color: var(--md-sys-color-on-surface); + margin: 0 0 8px; + letter-spacing: -0.02em; +} + +.features-subtitle { + font-size: var(--md-sys-typescale-body-large-size); + color: var(--md-sys-color-on-surface-variant); + margin: 0; +} + +.features-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 16px; +} + +.feature-card { + display: flex; + flex-direction: column; + gap: 12px; + padding: 24px; + background: var(--md-sys-color-surface); + border: 1px solid var(--md-sys-color-outline); + border-radius: var(--md-sys-shape-corner-medium); + box-shadow: var(--md-ref-elevation-1); + transition: box-shadow 0.2s, transform 0.2s; +} + +.feature-card:hover { + box-shadow: var(--md-ref-elevation-2); + transform: translateY(-2px); +} + +.feature-icon-wrapper { + display: flex; + align-items: center; + justify-content: center; + width: 56px; + height: 56px; + border-radius: var(--md-sys-shape-corner-medium); + background: var(--md-sys-color-primary-container); + color: var(--md-sys-color-primary); +} + +.feature-card-title { + font-size: 1.125rem; + font-weight: 500; + color: var(--md-sys-color-on-surface); + margin: 0; + line-height: 1.3; +} + +.feature-card-desc { + font-size: var(--md-sys-typescale-body-medium-size); + color: var(--md-sys-color-on-surface-variant); + margin: 0; + line-height: 1.6; +} + +/* ====== SUPPORTED SITES PAGE ====== */ + +.supported-page { + display: flex; + flex-direction: column; + gap: 24px; + padding: 24px 0; +} + +.supported-header { + text-align: center; +} + +.supported-title { + font-size: 2.25rem; + font-weight: 500; + color: var(--md-sys-color-on-surface); + margin: 0 0 8px; + letter-spacing: -0.02em; +} + +.supported-subtitle { + font-size: var(--md-sys-typescale-body-large-size); + color: var(--md-sys-color-on-surface-variant); + margin: 0; +} + +.supported-search { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 16px; + background: var(--md-sys-color-surface-variant); + border-radius: var(--md-sys-shape-corner-small); + border: 1px solid var(--md-sys-color-outline); + transition: border-color 0.2s, box-shadow 0.2s; + max-width: 480px; + margin: 0 auto; + width: 100%; +} + +.supported-search:focus-within { + border-color: var(--md-sys-color-primary); + box-shadow: inset 0 0 0 1px var(--md-sys-color-primary); +} + +.search-icon { + flex-shrink: 0; + color: var(--md-sys-color-on-surface-variant); +} + +.supported-input { + flex: 1; + font-family: 'Google Sans', 'Roboto', Arial, sans-serif; + font-size: var(--md-sys-typescale-body-large-size); + color: var(--md-sys-color-on-surface); + background: transparent; + border: none; + outline: none; +} + +.supported-input::placeholder { + color: var(--md-sys-color-on-surface-variant); + opacity: 0.7; +} + +.supported-sites { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); + gap: 8px; +} + +.site-card { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 16px; + background: var(--md-sys-color-surface); + border: 1px solid var(--md-sys-color-outline); + border-radius: var(--md-sys-shape-corner-small); + transition: box-shadow 0.2s, border-color 0.2s; +} + +.site-card:hover { + box-shadow: var(--md-ref-elevation-1); + border-color: var(--md-sys-color-primary); +} + +.site-card-icon { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: var(--md-sys-shape-corner-small); + background: var(--md-sys-color-primary-container); + color: var(--md-sys-color-primary); + flex-shrink: 0; +} + +.site-card-info { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} + +.site-card-name { + font-size: var(--md-sys-typescale-title-medium-size); + font-weight: 500; + color: var(--md-sys-color-on-surface); + line-height: 1.3; +} + +.site-card-url { + font-size: var(--md-sys-typescale-label-medium-size); + color: var(--md-sys-color-on-surface-variant); + text-decoration: none; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.site-card-url:hover { + color: var(--md-sys-color-primary); + text-decoration: underline; } -.blazor-error-boundary::after { - content: "An error has occurred." +.supported-empty { + grid-column: 1 / -1; + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + padding: 48px 0; + color: var(--md-sys-color-on-surface-variant); } -.darker-border-checkbox.form-check-input { - border-color: #929292; +.supported-empty p { + margin: 0; + font-size: var(--md-sys-typescale-body-large-size); } -.autoscrollable-wrapper { - overflow: auto; - max-height: 100%; +/* ====== ERROR PAGE ====== */ + +.error-page { display: flex; - flex-direction: column-reverse; + justify-content: center; + padding-top: 48px; +} + +.error-card { + background: var(--md-sys-color-surface); + border: 1px solid var(--md-sys-color-outline); + border-radius: var(--md-sys-shape-corner-medium); + padding: 32px; + max-width: 480px; + width: 100%; + text-align: center; + box-shadow: var(--md-ref-elevation-1); +} + +.error-title { + font-size: var(--md-sys-typescale-headline-medium-size); + font-weight: 500; + color: var(--md-sys-color-on-surface); + margin: 0 0 12px; +} + +.error-description { + font-size: var(--md-sys-typescale-body-large-size); + color: var(--md-sys-color-on-surface-variant); + margin: 0 0 8px; + line-height: 1.5; +} + +.error-details { + font-size: var(--md-sys-typescale-body-medium-size); + color: var(--md-sys-color-on-surface-variant); + margin: 0; +} + +.error-details code { + background: var(--md-sys-color-surface-variant); + padding: 2px 8px; + border-radius: var(--md-sys-shape-corner-extra-small); + font-family: 'Roboto Mono', monospace; + font-size: 0.8125rem; +} + +/* Loading progress bar */ +#blazor-loading { + position: fixed; + top: 0; + left: 0; + right: 0; + height: 3px; + background: transparent; + z-index: 9999; +} + +#blazor-loading::after { + content: ''; + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 30%; + background: linear-gradient(90deg, var(--md-sys-color-primary), #4285f4, #34a853, #fbbc04, #ea4335); + animation: loading-slide 1.5s ease-in-out infinite; + border-radius: 2px; +} + +@keyframes loading-slide { + 0% { left: -30%; } + 100% { left: 100%; } +} + +/* ====== RESPONSIVE ====== */ + +@media (max-width: 640px) { + .app-header-content { + flex-wrap: wrap; + height: auto; + padding: 8px 16px; + gap: 8px; + } + + .app-nav { + order: 3; + width: 100%; + justify-content: center; + margin-left: 0; + } + + .home-header { + padding: 24px 0 8px; + } + + .home-header h1 { + font-size: 1.75rem; + } + + .home-stats { + flex-direction: column; + align-items: center; + } + + .home-stat { + width: 100%; + max-width: 240px; + } + + .features-grid { + grid-template-columns: 1fr; + } + + .features-title { + font-size: 1.75rem; + } + + .features-page { + gap: 24px; + padding: 16px 0; + } + + .feature-card { + padding: 20px; + } + + .supported-title { + font-size: 1.75rem; + } + + .supported-sites { + grid-template-columns: 1fr; + } + + .supported-page { + gap: 16px; + padding: 16px 0; + } + + .format-cards { + gap: 8px; + } + + .format-card { + width: calc(50% - 4px); + min-width: 0; + padding: 16px 8px 12px; + } + + .format-card-icon { + width: 40px; + height: 40px; + } + + .format-card-icon svg { + width: 24px; + height: 24px; + } + + .form-section { + padding: 12px 16px; + } + + .form-actions { + padding: 12px 16px 16px; + } + + .range-fields { + flex-direction: column; + gap: 8px; + } + + .radio-group { + flex-direction: column; + gap: 4px; + } + + .google-radio { + padding: 10px 12px; + } + + .book-card { + flex-direction: column; + gap: 12px; + padding: 16px; + } + + .book-card-cover { + width: 96px; + min-width: 96px; + height: 144px; + } + + .book-card-title { + font-size: 1.125rem; + } + + .book-card-annotation { + max-height: 120px; + } + + .split-dropdown { + right: 0; + left: auto; + min-width: 180px; + } + + .error-card { + padding: 24px 16px; + } + + .error-page { + padding-top: 24px; + } + + .home-book-result { + margin-top: 16px; + } + + .app-footer-content { + padding: 20px 16px; + } + + .app-footer-links { + flex-direction: column; + gap: 8px; + } + + /* Adjust auth icons spacing on very small screens */ + .auth-icon { + margin-left: 8px; + } + + .auth-input { + padding: 10px 8px 10px 4px; + font-size: 16px; /* prevents iOS zoom on focus */ + } + + .google-input { + font-size: 16px; /* prevents iOS zoom on focus */ + } + + .site-card { + padding: 10px 12px; + } + + .button-log { + font-size: var(--md-sys-typescale-label-medium-size); + } +} + +@media (max-width: 380px) { + .format-card { + width: 100%; + } + + .format-cards { + gap: 6px; + } + + .auth-input-wrapper { + flex-wrap: nowrap; + } + + .auth-password-toggle { + width: 32px; + height: 32px; + } + + .book-card-cover { + width: 80px; + min-width: 80px; + height: 120px; + } + + .home-stat { + min-width: 0; + } + + .split-toggle { + width: 40px; + } } diff --git a/src/Elib2EbookWeb/wwwroot/bootstrap/bootstrap.min.css b/src/Elib2EbookWeb/wwwroot/bootstrap/bootstrap.min.css deleted file mode 100644 index 02ae65b5..00000000 --- a/src/Elib2EbookWeb/wwwroot/bootstrap/bootstrap.min.css +++ /dev/null @@ -1,7 +0,0 @@ -@charset "UTF-8";/*! - * Bootstrap v5.1.0 (https://getbootstrap.com/) - * Copyright 2011-2021 The Bootstrap Authors - * Copyright 2011-2021 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-body-rgb:33,37,41;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-bg:#fff}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:#6c757d}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{width:100%;padding-right:var(--bs-gutter-x,.75rem);padding-left:var(--bs-gutter-x,.75rem);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(var(--bs-gutter-y) * -1);margin-right:calc(var(--bs-gutter-x) * -.5);margin-left:calc(var(--bs-gutter-x) * -.5)}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.6666666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.6666666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.6666666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.6666666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-bg:transparent;--bs-table-accent-bg:transparent;--bs-table-striped-color:#212529;--bs-table-striped-bg:rgba(0, 0, 0, 0.05);--bs-table-active-color:#212529;--bs-table-active-bg:rgba(0, 0, 0, 0.1);--bs-table-hover-color:#212529;--bs-table-hover-bg:rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;color:#212529;vertical-align:top;border-color:#dee2e6}.table>:not(caption)>*>*{padding:.5rem .5rem;background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-accent-bg)}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table>:not(:last-child)>:last-child>*{border-bottom-color:currentColor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:1px 0}.table-bordered>:not(caption)>*>*{border-width:0 1px}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-striped>tbody>tr:nth-of-type(odd){--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-active{--bs-table-accent-bg:var(--bs-table-active-bg);color:var(--bs-table-active-color)}.table-hover>tbody>tr:hover{--bs-table-accent-bg:var(--bs-table-hover-bg);color:var(--bs-table-hover-color)}.table-primary{--bs-table-bg:#cfe2ff;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:#000;border-color:#bacbe6}.table-secondary{--bs-table-bg:#e2e3e5;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:#000;border-color:#cbccce}.table-success{--bs-table-bg:#d1e7dd;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:#000;border-color:#bcd0c7}.table-info{--bs-table-bg:#cff4fc;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:#000;border-color:#badce3}.table-warning{--bs-table-bg:#fff3cd;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:#000;border-color:#e6dbb9}.table-danger{--bs-table-bg:#f8d7da;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:#000;border-color:#dfc2c4}.table-light{--bs-table-bg:#f8f9fa;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:#000;border-color:#dfe0e1}.table-dark{--bs-table-bg:#212529;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:#fff;border-color:#373b3e}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:#6c757d}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:#212529;background-color:#fff;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{height:1.5em}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:#dde0e3}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:#dde0e3}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + 2px)}textarea.form-control-sm{min-height:calc(1.5em + .5rem + 2px)}textarea.form-control-lg{min-height:calc(1.5em + 1rem + 2px)}.form-control-color{width:3rem;height:auto;padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{height:1.5em;border-radius:.25rem}.form-control-color::-webkit-color-swatch{height:1.5em;border-radius:.25rem}.form-select{display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;-moz-padding-start:calc(0.75rem - 3px);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:#e9ecef}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #212529}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-input{width:1em;height:1em;margin-top:.25em;vertical-align:top;background-color:#fff;background-repeat:no-repeat;background-position:center;background-size:contain;border:1px solid rgba(0,0,0,.25);-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{width:2em;margin-left:-2.5em;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}.form-range{width:100%;height:1.5rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.form-range:disabled::-moz-range-thumb{background-color:#adb5bd}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-select{height:calc(3.5rem + 2px);line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;height:100%;padding:1rem .75rem;pointer-events:none;border:1px solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control{padding:1rem .75rem}.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus{z-index:3}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:3}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#198754}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(25,135,84,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#198754;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:#198754}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:#198754}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:#198754}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#198754}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group .form-control.is-valid,.input-group .form-select.is-valid,.was-validated .input-group .form-control:valid,.was-validated .input-group .form-select:valid{z-index:1}.input-group .form-control.is-valid:focus,.input-group .form-select.is-valid:focus,.was-validated .input-group .form-control:valid:focus,.was-validated .input-group .form-select:valid:focus{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:#dc3545}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:#dc3545}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:#dc3545}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group .form-control.is-invalid,.input-group .form-select.is-invalid,.was-validated .input-group .form-control:invalid,.was-validated .input-group .form-select:invalid{z-index:2}.input-group .form-control.is-invalid:focus,.input-group .form-select.is-invalid:focus,.was-validated .input-group .form-control:invalid:focus,.was-validated .input-group .form-select:invalid:focus{z-index:3}.btn{display:inline-block;font-weight:400;line-height:1.5;color:#212529;text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529}.btn-check:focus+.btn,.btn:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{pointer-events:none;opacity:.65}.btn-primary{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-primary:hover{color:#fff;background-color:#0b5ed7;border-color:#0a58ca}.btn-check:focus+.btn-primary,.btn-primary:focus{color:#fff;background-color:#0b5ed7;border-color:#0a58ca;box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-check:active+.btn-primary,.btn-check:checked+.btn-primary,.btn-primary.active,.btn-primary:active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0a58ca;border-color:#0a53be}.btn-check:active+.btn-primary:focus,.btn-check:checked+.btn-primary:focus,.btn-primary.active:focus,.btn-primary:active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5c636a;border-color:#565e64}.btn-check:focus+.btn-secondary,.btn-secondary:focus{color:#fff;background-color:#5c636a;border-color:#565e64;box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-check:active+.btn-secondary,.btn-check:checked+.btn-secondary,.btn-secondary.active,.btn-secondary:active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#565e64;border-color:#51585e}.btn-check:active+.btn-secondary:focus,.btn-check:checked+.btn-secondary:focus,.btn-secondary.active:focus,.btn-secondary:active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-success{color:#fff;background-color:#198754;border-color:#198754}.btn-success:hover{color:#fff;background-color:#157347;border-color:#146c43}.btn-check:focus+.btn-success,.btn-success:focus{color:#fff;background-color:#157347;border-color:#146c43;box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-check:active+.btn-success,.btn-check:checked+.btn-success,.btn-success.active,.btn-success:active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#146c43;border-color:#13653f}.btn-check:active+.btn-success:focus,.btn-check:checked+.btn-success:focus,.btn-success.active:focus,.btn-success:active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#198754;border-color:#198754}.btn-info{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-info:hover{color:#000;background-color:#31d2f2;border-color:#25cff2}.btn-check:focus+.btn-info,.btn-info:focus{color:#000;background-color:#31d2f2;border-color:#25cff2;box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-check:active+.btn-info,.btn-check:checked+.btn-info,.btn-info.active,.btn-info:active,.show>.btn-info.dropdown-toggle{color:#000;background-color:#3dd5f3;border-color:#25cff2}.btn-check:active+.btn-info:focus,.btn-check:checked+.btn-info:focus,.btn-info.active:focus,.btn-info:active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-info.disabled,.btn-info:disabled{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-warning{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#000;background-color:#ffca2c;border-color:#ffc720}.btn-check:focus+.btn-warning,.btn-warning:focus{color:#000;background-color:#ffca2c;border-color:#ffc720;box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-check:active+.btn-warning,.btn-check:checked+.btn-warning,.btn-warning.active,.btn-warning:active,.show>.btn-warning.dropdown-toggle{color:#000;background-color:#ffcd39;border-color:#ffc720}.btn-check:active+.btn-warning:focus,.btn-check:checked+.btn-warning:focus,.btn-warning.active:focus,.btn-warning:active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#bb2d3b;border-color:#b02a37}.btn-check:focus+.btn-danger,.btn-danger:focus{color:#fff;background-color:#bb2d3b;border-color:#b02a37;box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-check:active+.btn-danger,.btn-check:checked+.btn-danger,.btn-danger.active,.btn-danger:active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#b02a37;border-color:#a52834}.btn-check:active+.btn-danger:focus,.btn-check:checked+.btn-danger:focus,.btn-danger.active:focus,.btn-danger:active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-light{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:focus+.btn-light,.btn-light:focus{color:#000;background-color:#f9fafb;border-color:#f9fafb;box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-check:active+.btn-light,.btn-check:checked+.btn-light,.btn-light.active,.btn-light:active,.show>.btn-light.dropdown-toggle{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:active+.btn-light:focus,.btn-check:checked+.btn-light:focus,.btn-light.active:focus,.btn-light:active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-light.disabled,.btn-light:disabled{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-dark{color:#fff;background-color:#212529;border-color:#212529}.btn-dark:hover{color:#fff;background-color:#1c1f23;border-color:#1a1e21}.btn-check:focus+.btn-dark,.btn-dark:focus{color:#fff;background-color:#1c1f23;border-color:#1a1e21;box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-check:active+.btn-dark,.btn-check:checked+.btn-dark,.btn-dark.active,.btn-dark:active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1a1e21;border-color:#191c1f}.btn-check:active+.btn-dark:focus,.btn-check:checked+.btn-dark:focus,.btn-dark.active:focus,.btn-dark:active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#212529;border-color:#212529}.btn-outline-primary{color:#0d6efd;border-color:#0d6efd}.btn-outline-primary:hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:focus+.btn-outline-primary,.btn-outline-primary:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-check:active+.btn-outline-primary,.btn-check:checked+.btn-outline-primary,.btn-outline-primary.active,.btn-outline-primary.dropdown-toggle.show,.btn-outline-primary:active{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:active+.btn-outline-primary:focus,.btn-check:checked+.btn-outline-primary:focus,.btn-outline-primary.active:focus,.btn-outline-primary.dropdown-toggle.show:focus,.btn-outline-primary:active:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#0d6efd;background-color:transparent}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:focus+.btn-outline-secondary,.btn-outline-secondary:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-check:active+.btn-outline-secondary,.btn-check:checked+.btn-outline-secondary,.btn-outline-secondary.active,.btn-outline-secondary.dropdown-toggle.show,.btn-outline-secondary:active{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:active+.btn-outline-secondary:focus,.btn-check:checked+.btn-outline-secondary:focus,.btn-outline-secondary.active:focus,.btn-outline-secondary.dropdown-toggle.show:focus,.btn-outline-secondary:active:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-success{color:#198754;border-color:#198754}.btn-outline-success:hover{color:#fff;background-color:#198754;border-color:#198754}.btn-check:focus+.btn-outline-success,.btn-outline-success:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-check:active+.btn-outline-success,.btn-check:checked+.btn-outline-success,.btn-outline-success.active,.btn-outline-success.dropdown-toggle.show,.btn-outline-success:active{color:#fff;background-color:#198754;border-color:#198754}.btn-check:active+.btn-outline-success:focus,.btn-check:checked+.btn-outline-success:focus,.btn-outline-success.active:focus,.btn-outline-success.dropdown-toggle.show:focus,.btn-outline-success:active:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#198754;background-color:transparent}.btn-outline-info{color:#0dcaf0;border-color:#0dcaf0}.btn-outline-info:hover{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:focus+.btn-outline-info,.btn-outline-info:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-check:active+.btn-outline-info,.btn-check:checked+.btn-outline-info,.btn-outline-info.active,.btn-outline-info.dropdown-toggle.show,.btn-outline-info:active{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:active+.btn-outline-info:focus,.btn-check:checked+.btn-outline-info:focus,.btn-outline-info.active:focus,.btn-outline-info.dropdown-toggle.show:focus,.btn-outline-info:active:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#0dcaf0;background-color:transparent}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:focus+.btn-outline-warning,.btn-outline-warning:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-check:active+.btn-outline-warning,.btn-check:checked+.btn-outline-warning,.btn-outline-warning.active,.btn-outline-warning.dropdown-toggle.show,.btn-outline-warning:active{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:active+.btn-outline-warning:focus,.btn-check:checked+.btn-outline-warning:focus,.btn-outline-warning.active:focus,.btn-outline-warning.dropdown-toggle.show:focus,.btn-outline-warning:active:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:focus+.btn-outline-danger,.btn-outline-danger:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-check:active+.btn-outline-danger,.btn-check:checked+.btn-outline-danger,.btn-outline-danger.active,.btn-outline-danger.dropdown-toggle.show,.btn-outline-danger:active{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:active+.btn-outline-danger:focus,.btn-check:checked+.btn-outline-danger:focus,.btn-outline-danger.active:focus,.btn-outline-danger.dropdown-toggle.show:focus,.btn-outline-danger:active:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:focus+.btn-outline-light,.btn-outline-light:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-check:active+.btn-outline-light,.btn-check:checked+.btn-outline-light,.btn-outline-light.active,.btn-outline-light.dropdown-toggle.show,.btn-outline-light:active{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:active+.btn-outline-light:focus,.btn-check:checked+.btn-outline-light:focus,.btn-outline-light.active:focus,.btn-outline-light.dropdown-toggle.show:focus,.btn-outline-light:active:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-dark{color:#212529;border-color:#212529}.btn-outline-dark:hover{color:#fff;background-color:#212529;border-color:#212529}.btn-check:focus+.btn-outline-dark,.btn-outline-dark:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-check:active+.btn-outline-dark,.btn-check:checked+.btn-outline-dark,.btn-outline-dark.active,.btn-outline-dark.dropdown-toggle.show,.btn-outline-dark:active{color:#fff;background-color:#212529;border-color:#212529}.btn-check:active+.btn-outline-dark:focus,.btn-check:checked+.btn-outline-dark:focus,.btn-outline-dark.active:focus,.btn-outline-dark.dropdown-toggle.show:focus,.btn-outline-dark:active:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#212529;background-color:transparent}.btn-link{font-weight:400;color:#0d6efd;text-decoration:underline}.btn-link:hover{color:#0a58ca}.btn-link.disabled,.btn-link:disabled{color:#6c757d}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropend,.dropstart,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;z-index:1000;display:none;min-width:10rem;padding:.5rem 0;margin:0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:.125rem}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,.15)}.dropdown-item{display:block;width:100%;padding:.25rem 1rem;clear:both;font-weight:400;color:#212529;text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#1e2125;background-color:#e9ecef}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#0d6efd}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1rem;color:#212529}.dropdown-menu-dark{color:#dee2e6;background-color:#343a40;border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item{color:#dee2e6}.dropdown-menu-dark .dropdown-item:focus,.dropdown-menu-dark .dropdown-item:hover{color:#fff;background-color:rgba(255,255,255,.15)}.dropdown-menu-dark .dropdown-item.active,.dropdown-menu-dark .dropdown-item:active{color:#fff;background-color:#0d6efd}.dropdown-menu-dark .dropdown-item.disabled,.dropdown-menu-dark .dropdown-item:disabled{color:#adb5bd}.dropdown-menu-dark .dropdown-divider{border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item-text{color:#dee2e6}.dropdown-menu-dark .dropdown-header{color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem;color:#0d6efd;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:#0a58ca}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{margin-bottom:-1px;background:0 0;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6;isolation:isolate}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{background:0 0;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#0d6efd}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding-top:.5rem;padding-bottom:.5rem}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;text-decoration:none;white-space:nowrap}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem;transition:box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 .25rem}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas-header{display:none}.navbar-expand-sm .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-sm .offcanvas-bottom,.navbar-expand-sm .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas-header{display:none}.navbar-expand-md .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-md .offcanvas-bottom,.navbar-expand-md .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas-header{display:none}.navbar-expand-lg .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-lg .offcanvas-bottom,.navbar-expand-lg .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas-header{display:none}.navbar-expand-xl .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-xl .offcanvas-bottom,.navbar-expand-xl .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand-xxl .offcanvas-bottom,.navbar-expand-xxl .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas-header{display:none}.navbar-expand .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none}.navbar-expand .offcanvas-bottom,.navbar-expand .offcanvas-top{height:auto;border-top:0;border-bottom:0}.navbar-expand .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.55)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.55);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.55)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.55)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.55);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.55)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:1rem 1rem}.card-title{margin-bottom:.5rem}.card-subtitle{margin-top:-.25rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:1rem}.card-header{padding:.5rem 1rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.5rem 1rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.5rem;margin-bottom:-.5rem;margin-left:-.5rem;border-bottom:0}.card-header-pills{margin-right:-.5rem;margin-left:-.5rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-group>.card{margin-bottom:.75rem}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:1rem 1.25rem;font-size:1rem;color:#212529;text-align:left;background-color:#fff;border:0;border-radius:0;overflow-anchor:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,border-radius .15s ease}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:#0c63e4;background-color:#e7f1ff;box-shadow:inset 0 -1px 0 rgba(0,0,0,.125)}.accordion-button:not(.collapsed)::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");transform:rotate(-180deg)}.accordion-button::after{flex-shrink:0;width:1.25rem;height:1.25rem;margin-left:auto;content:"";background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-size:1.25rem;transition:transform .2s ease-in-out}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.accordion-header{margin-bottom:0}.accordion-item{background-color:#fff;border:1px solid rgba(0,0,0,.125)}.accordion-item:first-of-type{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.accordion-item:first-of-type .accordion-button{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.accordion-item:last-of-type .accordion-collapse{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.accordion-body{padding:1rem 1.25rem}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.accordion-flush .accordion-item .accordion-button{border-radius:0}.breadcrumb{display:flex;flex-wrap:wrap;padding:0 0;margin-bottom:1rem;list-style:none}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#6c757d;content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;color:#0d6efd;text-decoration:none;background-color:#fff;border:1px solid #dee2e6;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:#0a58ca;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;color:#0a58ca;background-color:#e9ecef;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.page-item:not(:first-child) .page-link{margin-left:-1px}.page-item.active .page-link{z-index:3;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;background-color:#fff;border-color:#dee2e6}.page-link{padding:.375rem .75rem}.page-item:first-child .page-link{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{position:relative;padding:1rem 1rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{color:#084298;background-color:#cfe2ff;border-color:#b6d4fe}.alert-primary .alert-link{color:#06357a}.alert-secondary{color:#41464b;background-color:#e2e3e5;border-color:#d3d6d8}.alert-secondary .alert-link{color:#34383c}.alert-success{color:#0f5132;background-color:#d1e7dd;border-color:#badbcc}.alert-success .alert-link{color:#0c4128}.alert-info{color:#055160;background-color:#cff4fc;border-color:#b6effb}.alert-info .alert-link{color:#04414d}.alert-warning{color:#664d03;background-color:#fff3cd;border-color:#ffecb5}.alert-warning .alert-link{color:#523e02}.alert-danger{color:#842029;background-color:#f8d7da;border-color:#f5c2c7}.alert-danger .alert-link{color:#6a1a21}.alert-light{color:#636464;background-color:#fefefe;border-color:#fdfdfe}.alert-light .alert-link{color:#4f5050}.alert-dark{color:#141619;background-color:#d3d3d4;border-color:#bcbebf}.alert-dark .alert-link{color:#101214}@-webkit-keyframes progress-bar-stripes{0%{background-position-x:1rem}}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress{display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#0d6efd;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:1s linear infinite progress-bar-stripes;animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>li::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.5rem 1rem;color:#212529;text-decoration:none;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#084298;background-color:#cfe2ff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#084298;background-color:#bacbe6}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#084298;border-color:#084298}.list-group-item-secondary{color:#41464b;background-color:#e2e3e5}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#41464b;background-color:#cbccce}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#41464b;border-color:#41464b}.list-group-item-success{color:#0f5132;background-color:#d1e7dd}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#0f5132;background-color:#bcd0c7}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#0f5132;border-color:#0f5132}.list-group-item-info{color:#055160;background-color:#cff4fc}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#055160;background-color:#badce3}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#055160;border-color:#055160}.list-group-item-warning{color:#664d03;background-color:#fff3cd}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#664d03;background-color:#e6dbb9}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#664d03;border-color:#664d03}.list-group-item-danger{color:#842029;background-color:#f8d7da}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#842029;background-color:#dfc2c4}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#842029;border-color:#842029}.list-group-item-light{color:#636464;background-color:#fefefe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#636464;background-color:#e5e5e5}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#636464;border-color:#636464}.list-group-item-dark{color:#141619;background-color:#d3d3d4}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#141619;background-color:#bebebf}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#141619;border-color:#141619}.btn-close{box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:#000;background:transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat;border:0;border-radius:.25rem;opacity:.5}.btn-close:hover{color:#000;text-decoration:none;opacity:.75}.btn-close:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);opacity:1}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:.25}.btn-close-white{filter:invert(1) grayscale(100%) brightness(200%)}.toast{width:350px;max-width:100%;font-size:.875rem;pointer-events:auto;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .5rem 1rem rgba(0,0,0,.15);border-radius:.25rem}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:.75rem}.toast-header{display:flex;align-items:center;padding:.5rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-header .btn-close{margin-right:-.375rem;margin-left:.75rem}.toast-body{padding:.75rem;word-wrap:break-word}.modal{position:fixed;top:0;left:0;z-index:1055;display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1050;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .btn-close{padding:.5rem .5rem;margin:-.5rem -.5rem -.5rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:1rem}.modal-footer{display:flex;flex-wrap:wrap;flex-shrink:0;align-items:center;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{height:calc(100% - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}.modal-fullscreen .modal-footer{border-radius:0}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}.modal-fullscreen-sm-down .modal-footer{border-radius:0}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}.modal-fullscreen-md-down .modal-footer{border-radius:0}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}.modal-fullscreen-lg-down .modal-footer{border-radius:0}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}.modal-fullscreen-xl-down .modal-footer{border-radius:0}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}.modal-fullscreen-xxl-down .modal-footer{border-radius:0}}.tooltip{position:absolute;z-index:1080;display:block;margin:0;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .tooltip-arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[data-popper-placement^=right],.bs-tooltip-end{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[data-popper-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[data-popper-placement^=left],.bs-tooltip-start{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1070;display:block;max-width:276px;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .popover-arrow{position:absolute;display:block;width:1rem;height:.5rem}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f0f0f0}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem 1rem;margin-bottom:0;font-size:1rem;background-color:#f0f0f0;border-bottom:1px solid rgba(0,0,0,.2);border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:1rem 1rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%;list-style:none}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}@-webkit-keyframes spinner-border{to{transform:rotate(360deg)}}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:.75s linear infinite spinner-border;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:.75s linear infinite spinner-grow;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{-webkit-animation-duration:1.5s;animation-duration:1.5s}}.offcanvas{position:fixed;bottom:0;z-index:1045;display:flex;flex-direction:column;max-width:100%;visibility:hidden;background-color:#fff;background-clip:padding-box;outline:0;transition:transform .3s ease-in-out}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;justify-content:space-between;padding:1rem 1rem}.offcanvas-header .btn-close{padding:.5rem .5rem;margin-top:-.5rem;margin-right:-.5rem;margin-bottom:-.5rem}.offcanvas-title{margin-bottom:0;line-height:1.5}.offcanvas-body{flex-grow:1;padding:1rem 1rem;overflow-y:auto}.offcanvas-start{top:0;left:0;width:400px;border-right:1px solid rgba(0,0,0,.2);transform:translateX(-100%)}.offcanvas-end{top:0;right:0;width:400px;border-left:1px solid rgba(0,0,0,.2);transform:translateX(100%)}.offcanvas-top{top:0;right:0;left:0;height:30vh;max-height:100%;border-bottom:1px solid rgba(0,0,0,.2);transform:translateY(-100%)}.offcanvas-bottom{right:0;left:0;height:30vh;max-height:100%;border-top:1px solid rgba(0,0,0,.2);transform:translateY(100%)}.offcanvas.show{transform:none}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentColor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{-webkit-animation:placeholder-glow 2s ease-in-out infinite;animation:placeholder-glow 2s ease-in-out infinite}@-webkit-keyframes placeholder-glow{50%{opacity:.2}}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;-webkit-animation:placeholder-wave 2s linear infinite;animation:placeholder-wave 2s linear infinite}@-webkit-keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.link-primary{color:#0d6efd}.link-primary:focus,.link-primary:hover{color:#0a58ca}.link-secondary{color:#6c757d}.link-secondary:focus,.link-secondary:hover{color:#565e64}.link-success{color:#198754}.link-success:focus,.link-success:hover{color:#146c43}.link-info{color:#0dcaf0}.link-info:focus,.link-info:hover{color:#3dd5f3}.link-warning{color:#ffc107}.link-warning:focus,.link-warning:hover{color:#ffcd39}.link-danger{color:#dc3545}.link-danger:focus,.link-danger:hover{color:#b02a37}.link-light{color:#f8f9fa}.link-light:focus,.link-light:hover{color:#f9fafb}.link-dark{color:#212529}.link-dark:focus,.link-dark:hover{color:#1a1e21}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:calc(3 / 4 * 100%)}.ratio-16x9{--bs-aspect-ratio:calc(9 / 16 * 100%)}.ratio-21x9{--bs-aspect-ratio:calc(9 / 21 * 100%)}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:1px;min-height:1em;background-color:currentColor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:1px solid #dee2e6!important}.border-0{border:0!important}.border-top{border-top:1px solid #dee2e6!important}.border-top-0{border-top:0!important}.border-end{border-right:1px solid #dee2e6!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:1px solid #dee2e6!important}.border-start-0{border-left:0!important}.border-primary{border-color:#0d6efd!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#198754!important}.border-info{border-color:#0dcaf0!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#212529!important}.border-white{border-color:#fff!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-light{font-weight:300!important}.fw-lighter{font-weight:lighter!important}.fw-normal{font-weight:400!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:#6c757d!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:.25rem!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:.2rem!important}.rounded-2{border-radius:.25rem!important}.rounded-3{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-end{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-start{border-bottom-left-radius:.25rem!important;border-top-left-radius:.25rem!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} -/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/src/Elib2EbookWeb/wwwroot/bootstrap/bootstrap.min.css.map b/src/Elib2EbookWeb/wwwroot/bootstrap/bootstrap.min.css.map deleted file mode 100644 index afcd9e33..00000000 --- a/src/Elib2EbookWeb/wwwroot/bootstrap/bootstrap.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../scss/bootstrap.scss","../../scss/_root.scss","../../scss/_reboot.scss","dist/css/bootstrap.css","../../scss/vendor/_rfs.scss","../../scss/mixins/_border-radius.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/_containers.scss","../../scss/mixins/_container.scss","../../scss/mixins/_breakpoints.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/_tables.scss","../../scss/mixins/_table-variants.scss","../../scss/forms/_labels.scss","../../scss/forms/_form-text.scss","../../scss/forms/_form-control.scss","../../scss/mixins/_transition.scss","../../scss/mixins/_gradients.scss","../../scss/forms/_form-select.scss","../../scss/forms/_form-check.scss","../../scss/forms/_form-range.scss","../../scss/forms/_floating-labels.scss","../../scss/forms/_input-group.scss","../../scss/mixins/_forms.scss","../../scss/_buttons.scss","../../scss/mixins/_buttons.scss","../../scss/_transitions.scss","../../scss/_dropdown.scss","../../scss/mixins/_caret.scss","../../scss/_button-group.scss","../../scss/_nav.scss","../../scss/_navbar.scss","../../scss/_card.scss","../../scss/_accordion.scss","../../scss/_breadcrumb.scss","../../scss/_pagination.scss","../../scss/mixins/_pagination.scss","../../scss/_badge.scss","../../scss/_alert.scss","../../scss/mixins/_alert.scss","../../scss/_progress.scss","../../scss/_list-group.scss","../../scss/mixins/_list-group.scss","../../scss/_close.scss","../../scss/_toasts.scss","../../scss/_modal.scss","../../scss/mixins/_backdrop.scss","../../scss/_tooltip.scss","../../scss/mixins/_reset-text.scss","../../scss/_popover.scss","../../scss/_carousel.scss","../../scss/mixins/_clearfix.scss","../../scss/_spinners.scss","../../scss/_offcanvas.scss","../../scss/_placeholders.scss","../../scss/helpers/_colored-links.scss","../../scss/helpers/_ratio.scss","../../scss/helpers/_position.scss","../../scss/helpers/_stacks.scss","../../scss/helpers/_visually-hidden.scss","../../scss/mixins/_visually-hidden.scss","../../scss/helpers/_stretched-link.scss","../../scss/helpers/_text-truncation.scss","../../scss/mixins/_text-truncate.scss","../../scss/helpers/_vr.scss","../../scss/mixins/_utilities.scss","../../scss/utilities/_api.scss"],"names":[],"mappings":"iBAAA;;;;;ACAA,MAQI,UAAA,QAAA,YAAA,QAAA,YAAA,QAAA,UAAA,QAAA,SAAA,QAAA,YAAA,QAAA,YAAA,QAAA,WAAA,QAAA,UAAA,QAAA,UAAA,QAAA,WAAA,KAAA,UAAA,QAAA,eAAA,QAIA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAIA,aAAA,QAAA,eAAA,QAAA,aAAA,QAAA,UAAA,QAAA,aAAA,QAAA,YAAA,QAAA,WAAA,QAAA,UAAA,QAIA,iBAAA,EAAA,CAAA,GAAA,CAAA,IAAA,mBAAA,GAAA,CAAA,GAAA,CAAA,IAAA,iBAAA,EAAA,CAAA,GAAA,CAAA,GAAA,cAAA,EAAA,CAAA,GAAA,CAAA,IAAA,iBAAA,GAAA,CAAA,GAAA,CAAA,EAAA,gBAAA,GAAA,CAAA,EAAA,CAAA,GAAA,eAAA,GAAA,CAAA,GAAA,CAAA,IAAA,cAAA,EAAA,CAAA,EAAA,CAAA,GAGF,eAAA,GAAA,CAAA,GAAA,CAAA,IACA,eAAA,CAAA,CAAA,CAAA,CAAA,EACA,cAAA,EAAA,CAAA,EAAA,CAAA,GAMA,qBAAA,SAAA,CAAA,aAAA,CAAA,UAAA,CAAA,MAAA,CAAA,gBAAA,CAAA,KAAA,CAAA,WAAA,CAAA,iBAAA,CAAA,UAAA,CAAA,mBAAA,CAAA,gBAAA,CAAA,iBAAA,CAAA,mBACA,oBAAA,cAAA,CAAA,KAAA,CAAA,MAAA,CAAA,QAAA,CAAA,iBAAA,CAAA,aAAA,CAAA,UACA,cAAA,2EAQA,sBAAA,0BACA,oBAAA,KACA,sBAAA,IACA,sBAAA,IACA,gBAAA,QAIA,aAAA,KClCF,EC+CA,QADA,SD3CE,WAAA,WAeE,8CANJ,MAOM,gBAAA,QAcN,KACE,OAAA,EACA,YAAA,2BEmPI,UAAA,yBFjPJ,YAAA,2BACA,YAAA,2BACA,MAAA,qBACA,WAAA,0BACA,iBAAA,kBACA,yBAAA,KACA,4BAAA,YAUF,GACE,OAAA,KAAA,EACA,MAAA,QACA,iBAAA,aACA,OAAA,EACA,QAAA,IAGF,eACE,OAAA,IAUF,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GACE,WAAA,EACA,cAAA,MAGA,YAAA,IACA,YAAA,IAIF,IAAA,GEwMQ,UAAA,uBAlKJ,0BFtCJ,IAAA,GE+MQ,UAAA,QF1MR,IAAA,GEmMQ,UAAA,sBAlKJ,0BFjCJ,IAAA,GE0MQ,UAAA,MFrMR,IAAA,GE8LQ,UAAA,oBAlKJ,0BF5BJ,IAAA,GEqMQ,UAAA,SFhMR,IAAA,GEyLQ,UAAA,sBAlKJ,0BFvBJ,IAAA,GEgMQ,UAAA,QF3LR,IAAA,GEgLM,UAAA,QF3KN,IAAA,GE2KM,UAAA,KFhKN,EACE,WAAA,EACA,cAAA,KCmBF,6BDRA,YAEE,wBAAA,UAAA,OAAA,gBAAA,UAAA,OACA,OAAA,KACA,iCAAA,KAAA,yBAAA,KAMF,QACE,cAAA,KACA,WAAA,OACA,YAAA,QAMF,GCIA,GDFE,aAAA,KCQF,GDLA,GCIA,GDDE,WAAA,EACA,cAAA,KAGF,MCKA,MACA,MAFA,MDAE,cAAA,EAGF,GACE,YAAA,IAKF,GACE,cAAA,MACA,YAAA,EAMF,WACE,OAAA,EAAA,EAAA,KAQF,ECNA,ODQE,YAAA,OAQF,OAAA,ME4EM,UAAA,OFrEN,MAAA,KACE,QAAA,KACA,iBAAA,QASF,ICpBA,IDsBE,SAAA,SEwDI,UAAA,MFtDJ,YAAA,EACA,eAAA,SAGF,IAAM,OAAA,OACN,IAAM,IAAA,MAKN,EACE,MAAA,QACA,gBAAA,UAEA,QACE,MAAA,QAWF,2BAAA,iCAEE,MAAA,QACA,gBAAA,KCxBJ,KACA,ID8BA,IC7BA,KDiCE,YAAA,yBEcI,UAAA,IFZJ,UAAA,IACA,aAAA,cAOF,IACE,QAAA,MACA,WAAA,EACA,cAAA,KACA,SAAA,KEAI,UAAA,OFKJ,SELI,UAAA,QFOF,MAAA,QACA,WAAA,OAIJ,KEZM,UAAA,OFcJ,MAAA,QACA,UAAA,WAGA,OACE,MAAA,QAIJ,IACE,QAAA,MAAA,MExBI,UAAA,OF0BJ,MAAA,KACA,iBAAA,QG7SE,cAAA,MHgTF,QACE,QAAA,EE/BE,UAAA,IFiCF,YAAA,IASJ,OACE,OAAA,EAAA,EAAA,KAMF,ICjDA,IDmDE,eAAA,OAQF,MACE,aAAA,OACA,gBAAA,SAGF,QACE,YAAA,MACA,eAAA,MACA,MAAA,QACA,WAAA,KAOF,GAEE,WAAA,QACA,WAAA,qBCxDF,MAGA,GAFA,MAGA,GDuDA,MCzDA,GD+DE,aAAA,QACA,aAAA,MACA,aAAA,EAQF,MACE,QAAA,aAMF,OAEE,cAAA,EAQF,iCACE,QAAA,ECtEF,OD2EA,MCzEA,SADA,OAEA,SD6EE,OAAA,EACA,YAAA,QE9HI,UAAA,QFgIJ,YAAA,QAIF,OC5EA,OD8EE,eAAA,KAKF,cACE,OAAA,QAGF,OAGE,UAAA,OAGA,gBACE,QAAA,EAOJ,0CACE,QAAA,KClFF,cACA,aACA,cDwFA,OAIE,mBAAA,OCxFF,6BACA,4BACA,6BDyFI,sBACE,OAAA,QAON,mBACE,QAAA,EACA,aAAA,KAKF,SACE,OAAA,SAUF,SACE,UAAA,EACA,QAAA,EACA,OAAA,EACA,OAAA,EAQF,OACE,MAAA,KACA,MAAA,KACA,QAAA,EACA,cAAA,MEnNM,UAAA,sBFsNN,YAAA,QExXE,0BFiXJ,OExMQ,UAAA,QFiNN,SACE,MAAA,KChGJ,kCDuGA,uCCxGA,mCADA,+BAGA,oCAJA,6BAKA,mCD4GE,QAAA,EAGF,4BACE,OAAA,KASF,cACE,eAAA,KACA,mBAAA,UAmBF,4BACE,mBAAA,KAKF,+BACE,QAAA,EAMF,uBACE,KAAA,QAMF,6BACE,KAAA,QACA,mBAAA,OAKF,OACE,QAAA,aAKF,OACE,OAAA,EAOF,QACE,QAAA,UACA,OAAA,QAQF,SACE,eAAA,SAQF,SACE,QAAA,eInlBF,MFyQM,UAAA,QEvQJ,YAAA,IAKA,WFsQM,UAAA,uBEpQJ,YAAA,IACA,YAAA,IFiGA,0BEpGF,WF6QM,UAAA,ME7QN,WFsQM,UAAA,uBEpQJ,YAAA,IACA,YAAA,IFiGA,0BEpGF,WF6QM,UAAA,QE7QN,WFsQM,UAAA,uBEpQJ,YAAA,IACA,YAAA,IFiGA,0BEpGF,WF6QM,UAAA,ME7QN,WFsQM,UAAA,uBEpQJ,YAAA,IACA,YAAA,IFiGA,0BEpGF,WF6QM,UAAA,QE7QN,WFsQM,UAAA,uBEpQJ,YAAA,IACA,YAAA,IFiGA,0BEpGF,WF6QM,UAAA,ME7QN,WFsQM,UAAA,uBEpQJ,YAAA,IACA,YAAA,IFiGA,0BEpGF,WF6QM,UAAA,QEvPR,eCrDE,aAAA,EACA,WAAA,KDyDF,aC1DE,aAAA,EACA,WAAA,KD4DF,kBACE,QAAA,aAEA,mCACE,aAAA,MAUJ,YFsNM,UAAA,OEpNJ,eAAA,UAIF,YACE,cAAA,KF+MI,UAAA,QE5MJ,wBACE,cAAA,EAIJ,mBACE,WAAA,MACA,cAAA,KFqMI,UAAA,OEnMJ,MAAA,QAEA,2BACE,QAAA,KE9FJ,WCIE,UAAA,KAGA,OAAA,KDDF,eACE,QAAA,OACA,iBAAA,KACA,OAAA,IAAA,MAAA,QHGE,cAAA,OIRF,UAAA,KAGA,OAAA,KDcF,QAEE,QAAA,aAGF,YACE,cAAA,MACA,YAAA,EAGF,gBJ+PM,UAAA,OI7PJ,MAAA,QElCA,WPqmBF,iBAGA,cACA,cACA,cAHA,cADA,eQzmBE,MAAA,KACA,cAAA,0BACA,aAAA,0BACA,aAAA,KACA,YAAA,KCwDE,yBF5CE,WAAA,cACE,UAAA,OE2CJ,yBF5CE,WAAA,cAAA,cACE,UAAA,OE2CJ,yBF5CE,WAAA,cAAA,cAAA,cACE,UAAA,OE2CJ,0BF5CE,WAAA,cAAA,cAAA,cAAA,cACE,UAAA,QE2CJ,0BF5CE,WAAA,cAAA,cAAA,cAAA,cAAA,eACE,UAAA,QGfN,KCAA,cAAA,OACA,cAAA,EACA,QAAA,KACA,UAAA,KACA,WAAA,8BACA,aAAA,+BACA,YAAA,+BDHE,OCYF,YAAA,EACA,MAAA,KACA,UAAA,KACA,cAAA,8BACA,aAAA,8BACA,WAAA,mBA+CI,KACE,KAAA,EAAA,EAAA,GAGF,iBApCJ,KAAA,EAAA,EAAA,KACA,MAAA,KAcA,cACE,KAAA,EAAA,EAAA,KACA,MAAA,KAFF,cACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,cACE,KAAA,EAAA,EAAA,KACA,MAAA,eAFF,cACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,cACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,cACE,KAAA,EAAA,EAAA,KACA,MAAA,eA+BE,UAhDJ,KAAA,EAAA,EAAA,KACA,MAAA,KAqDQ,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,YA+DM,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,QAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,QAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,QAhEN,KAAA,EAAA,EAAA,KACA,MAAA,KAuEQ,UAxDV,YAAA,YAwDU,UAxDV,YAAA,aAwDU,UAxDV,YAAA,IAwDU,UAxDV,YAAA,aAwDU,UAxDV,YAAA,aAwDU,UAxDV,YAAA,IAwDU,UAxDV,YAAA,aAwDU,UAxDV,YAAA,aAwDU,UAxDV,YAAA,IAwDU,WAxDV,YAAA,aAwDU,WAxDV,YAAA,aAmEM,KXusBR,MWrsBU,cAAA,EAGF,KXusBR,MWrsBU,cAAA,EAPF,KXitBR,MW/sBU,cAAA,QAGF,KXitBR,MW/sBU,cAAA,QAPF,KX2tBR,MWztBU,cAAA,OAGF,KX2tBR,MWztBU,cAAA,OAPF,KXquBR,MWnuBU,cAAA,KAGF,KXquBR,MWnuBU,cAAA,KAPF,KX+uBR,MW7uBU,cAAA,OAGF,KX+uBR,MW7uBU,cAAA,OAPF,KXyvBR,MWvvBU,cAAA,KAGF,KXyvBR,MWvvBU,cAAA,KFzDN,yBESE,QACE,KAAA,EAAA,EAAA,GAGF,oBApCJ,KAAA,EAAA,EAAA,KACA,MAAA,KAcA,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,KAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,eAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,eA+BE,aAhDJ,KAAA,EAAA,EAAA,KACA,MAAA,KAqDQ,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,YA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,KAuEQ,aAxDV,YAAA,EAwDU,aAxDV,YAAA,YAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,aAmEM,QX45BR,SW15BU,cAAA,EAGF,QX45BR,SW15BU,cAAA,EAPF,QXs6BR,SWp6BU,cAAA,QAGF,QXs6BR,SWp6BU,cAAA,QAPF,QXg7BR,SW96BU,cAAA,OAGF,QXg7BR,SW96BU,cAAA,OAPF,QX07BR,SWx7BU,cAAA,KAGF,QX07BR,SWx7BU,cAAA,KAPF,QXo8BR,SWl8BU,cAAA,OAGF,QXo8BR,SWl8BU,cAAA,OAPF,QX88BR,SW58BU,cAAA,KAGF,QX88BR,SW58BU,cAAA,MFzDN,yBESE,QACE,KAAA,EAAA,EAAA,GAGF,oBApCJ,KAAA,EAAA,EAAA,KACA,MAAA,KAcA,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,KAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,eAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,eA+BE,aAhDJ,KAAA,EAAA,EAAA,KACA,MAAA,KAqDQ,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,YA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,KAuEQ,aAxDV,YAAA,EAwDU,aAxDV,YAAA,YAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,aAmEM,QXinCR,SW/mCU,cAAA,EAGF,QXinCR,SW/mCU,cAAA,EAPF,QX2nCR,SWznCU,cAAA,QAGF,QX2nCR,SWznCU,cAAA,QAPF,QXqoCR,SWnoCU,cAAA,OAGF,QXqoCR,SWnoCU,cAAA,OAPF,QX+oCR,SW7oCU,cAAA,KAGF,QX+oCR,SW7oCU,cAAA,KAPF,QXypCR,SWvpCU,cAAA,OAGF,QXypCR,SWvpCU,cAAA,OAPF,QXmqCR,SWjqCU,cAAA,KAGF,QXmqCR,SWjqCU,cAAA,MFzDN,yBESE,QACE,KAAA,EAAA,EAAA,GAGF,oBApCJ,KAAA,EAAA,EAAA,KACA,MAAA,KAcA,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,KAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,eAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,eA+BE,aAhDJ,KAAA,EAAA,EAAA,KACA,MAAA,KAqDQ,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,YA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,KAuEQ,aAxDV,YAAA,EAwDU,aAxDV,YAAA,YAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,aAmEM,QXs0CR,SWp0CU,cAAA,EAGF,QXs0CR,SWp0CU,cAAA,EAPF,QXg1CR,SW90CU,cAAA,QAGF,QXg1CR,SW90CU,cAAA,QAPF,QX01CR,SWx1CU,cAAA,OAGF,QX01CR,SWx1CU,cAAA,OAPF,QXo2CR,SWl2CU,cAAA,KAGF,QXo2CR,SWl2CU,cAAA,KAPF,QX82CR,SW52CU,cAAA,OAGF,QX82CR,SW52CU,cAAA,OAPF,QXw3CR,SWt3CU,cAAA,KAGF,QXw3CR,SWt3CU,cAAA,MFzDN,0BESE,QACE,KAAA,EAAA,EAAA,GAGF,oBApCJ,KAAA,EAAA,EAAA,KACA,MAAA,KAcA,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,KAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,eAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,eA+BE,aAhDJ,KAAA,EAAA,EAAA,KACA,MAAA,KAqDQ,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,YA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,KAuEQ,aAxDV,YAAA,EAwDU,aAxDV,YAAA,YAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,aAmEM,QX2hDR,SWzhDU,cAAA,EAGF,QX2hDR,SWzhDU,cAAA,EAPF,QXqiDR,SWniDU,cAAA,QAGF,QXqiDR,SWniDU,cAAA,QAPF,QX+iDR,SW7iDU,cAAA,OAGF,QX+iDR,SW7iDU,cAAA,OAPF,QXyjDR,SWvjDU,cAAA,KAGF,QXyjDR,SWvjDU,cAAA,KAPF,QXmkDR,SWjkDU,cAAA,OAGF,QXmkDR,SWjkDU,cAAA,OAPF,QX6kDR,SW3kDU,cAAA,KAGF,QX6kDR,SW3kDU,cAAA,MFzDN,0BESE,SACE,KAAA,EAAA,EAAA,GAGF,qBApCJ,KAAA,EAAA,EAAA,KACA,MAAA,KAcA,kBACE,KAAA,EAAA,EAAA,KACA,MAAA,KAFF,kBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,kBACE,KAAA,EAAA,EAAA,KACA,MAAA,eAFF,kBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,kBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,kBACE,KAAA,EAAA,EAAA,KACA,MAAA,eA+BE,cAhDJ,KAAA,EAAA,EAAA,KACA,MAAA,KAqDQ,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,YA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,YAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,YAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,YAhEN,KAAA,EAAA,EAAA,KACA,MAAA,KAuEQ,cAxDV,YAAA,EAwDU,cAxDV,YAAA,YAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,IAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,IAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,IAwDU,eAxDV,YAAA,aAwDU,eAxDV,YAAA,aAmEM,SXgvDR,UW9uDU,cAAA,EAGF,SXgvDR,UW9uDU,cAAA,EAPF,SX0vDR,UWxvDU,cAAA,QAGF,SX0vDR,UWxvDU,cAAA,QAPF,SXowDR,UWlwDU,cAAA,OAGF,SXowDR,UWlwDU,cAAA,OAPF,SX8wDR,UW5wDU,cAAA,KAGF,SX8wDR,UW5wDU,cAAA,KAPF,SXwxDR,UWtxDU,cAAA,OAGF,SXwxDR,UWtxDU,cAAA,OAPF,SXkyDR,UWhyDU,cAAA,KAGF,SXkyDR,UWhyDU,cAAA,MCpHV,OACE,cAAA,YACA,qBAAA,YACA,yBAAA,QACA,sBAAA,oBACA,wBAAA,QACA,qBAAA,mBACA,uBAAA,QACA,oBAAA,qBAEA,MAAA,KACA,cAAA,KACA,MAAA,QACA,eAAA,IACA,aAAA,QAOA,yBACE,QAAA,MAAA,MACA,iBAAA,mBACA,oBAAA,IACA,WAAA,MAAA,EAAA,EAAA,EAAA,OAAA,0BAGF,aACE,eAAA,QAGF,aACE,eAAA,OAIF,uCACE,oBAAA,aASJ,aACE,aAAA,IAUA,4BACE,QAAA,OAAA,OAeF,gCACE,aAAA,IAAA,EAGA,kCACE,aAAA,EAAA,IAOJ,oCACE,oBAAA,EASF,yCACE,qBAAA,2BACA,MAAA,8BAQJ,cACE,qBAAA,0BACA,MAAA,6BAQA,4BACE,qBAAA,yBACA,MAAA,4BCxHF,eAME,cAAA,QACA,sBAAA,QACA,yBAAA,KACA,qBAAA,QACA,wBAAA,KACA,oBAAA,QACA,uBAAA,KAEA,MAAA,KACA,aAAA,QAfF,iBAME,cAAA,QACA,sBAAA,QACA,yBAAA,KACA,qBAAA,QACA,wBAAA,KACA,oBAAA,QACA,uBAAA,KAEA,MAAA,KACA,aAAA,QAfF,eAME,cAAA,QACA,sBAAA,QACA,yBAAA,KACA,qBAAA,QACA,wBAAA,KACA,oBAAA,QACA,uBAAA,KAEA,MAAA,KACA,aAAA,QAfF,YAME,cAAA,QACA,sBAAA,QACA,yBAAA,KACA,qBAAA,QACA,wBAAA,KACA,oBAAA,QACA,uBAAA,KAEA,MAAA,KACA,aAAA,QAfF,eAME,cAAA,QACA,sBAAA,QACA,yBAAA,KACA,qBAAA,QACA,wBAAA,KACA,oBAAA,QACA,uBAAA,KAEA,MAAA,KACA,aAAA,QAfF,cAME,cAAA,QACA,sBAAA,QACA,yBAAA,KACA,qBAAA,QACA,wBAAA,KACA,oBAAA,QACA,uBAAA,KAEA,MAAA,KACA,aAAA,QAfF,aAME,cAAA,QACA,sBAAA,QACA,yBAAA,KACA,qBAAA,QACA,wBAAA,KACA,oBAAA,QACA,uBAAA,KAEA,MAAA,KACA,aAAA,QAfF,YAME,cAAA,QACA,sBAAA,QACA,yBAAA,KACA,qBAAA,QACA,wBAAA,KACA,oBAAA,QACA,uBAAA,KAEA,MAAA,KACA,aAAA,QDgIA,kBACE,WAAA,KACA,2BAAA,MHvEF,4BGqEA,qBACE,WAAA,KACA,2BAAA,OHvEF,4BGqEA,qBACE,WAAA,KACA,2BAAA,OHvEF,4BGqEA,qBACE,WAAA,KACA,2BAAA,OHvEF,6BGqEA,qBACE,WAAA,KACA,2BAAA,OHvEF,6BGqEA,sBACE,WAAA,KACA,2BAAA,OE/IN,YACE,cAAA,MASF,gBACE,YAAA,oBACA,eAAA,oBACA,cAAA,EboRI,UAAA,QahRJ,YAAA,IAIF,mBACE,YAAA,kBACA,eAAA,kBb0QI,UAAA,QatQN,mBACE,YAAA,mBACA,eAAA,mBboQI,UAAA,QcjSN,WACE,WAAA,OdgSI,UAAA,Oc5RJ,MAAA,QCLF,cACE,QAAA,MACA,MAAA,KACA,QAAA,QAAA,Of8RI,UAAA,Ke3RJ,YAAA,IACA,YAAA,IACA,MAAA,QACA,iBAAA,KACA,gBAAA,YACA,OAAA,IAAA,MAAA,QACA,mBAAA,KAAA,gBAAA,KAAA,WAAA,KdGE,cAAA,OeHE,WAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAIA,uCDhBN,cCiBQ,WAAA,MDGN,yBACE,SAAA,OAEA,wDACE,OAAA,QAKJ,oBACE,MAAA,QACA,iBAAA,KACA,aAAA,QACA,QAAA,EAKE,WAAA,EAAA,EAAA,EAAA,OAAA,qBAOJ,2CAEE,OAAA,MAIF,gCACE,MAAA,QAEA,QAAA,EAHF,2BACE,MAAA,QAEA,QAAA,EAQF,uBAAA,wBAEE,iBAAA,QAGA,QAAA,EAIF,oCACE,QAAA,QAAA,OACA,OAAA,SAAA,QACA,mBAAA,OAAA,kBAAA,OACA,MAAA,QE3EF,iBAAA,QF6EE,eAAA,KACA,aAAA,QACA,aAAA,MACA,aAAA,EACA,wBAAA,IACA,cAAA,ECtEE,WAAA,MAAA,KAAA,WAAA,CAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAIA,uCDuDJ,oCCtDM,WAAA,MDqEN,yEACE,iBAAA,QAGF,0CACE,QAAA,QAAA,OACA,OAAA,SAAA,QACA,mBAAA,OAAA,kBAAA,OACA,MAAA,QE9FF,iBAAA,QFgGE,eAAA,KACA,aAAA,QACA,aAAA,MACA,aAAA,EACA,wBAAA,IACA,cAAA,ECzFE,mBAAA,MAAA,KAAA,WAAA,CAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAAA,WAAA,MAAA,KAAA,WAAA,CAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAIA,uCD0EJ,0CCzEM,mBAAA,KAAA,WAAA,MDwFN,+EACE,iBAAA,QASJ,wBACE,QAAA,MACA,MAAA,KACA,QAAA,QAAA,EACA,cAAA,EACA,YAAA,IACA,MAAA,QACA,iBAAA,YACA,OAAA,MAAA,YACA,aAAA,IAAA,EAEA,wCAAA,wCAEE,cAAA,EACA,aAAA,EAWJ,iBACE,WAAA,0BACA,QAAA,OAAA,MfmJI,UAAA,QClRF,cAAA,McmIF,uCACE,QAAA,OAAA,MACA,OAAA,QAAA,OACA,mBAAA,MAAA,kBAAA,MAGF,6CACE,QAAA,OAAA,MACA,OAAA,QAAA,OACA,mBAAA,MAAA,kBAAA,MAIJ,iBACE,WAAA,yBACA,QAAA,MAAA,KfgII,UAAA,QClRF,cAAA,McsJF,uCACE,QAAA,MAAA,KACA,OAAA,OAAA,MACA,mBAAA,KAAA,kBAAA,KAGF,6CACE,QAAA,MAAA,KACA,OAAA,OAAA,MACA,mBAAA,KAAA,kBAAA,KAQF,sBACE,WAAA,2BAGF,yBACE,WAAA,0BAGF,yBACE,WAAA,yBAKJ,oBACE,MAAA,KACA,OAAA,KACA,QAAA,QAEA,mDACE,OAAA,QAGF,uCACE,OAAA,Md/LA,cAAA,OcmMF,0CACE,OAAA,MdpMA,cAAA,OiBdJ,aACE,QAAA,MACA,MAAA,KACA,QAAA,QAAA,QAAA,QAAA,OAEA,mBAAA,oBlB2RI,UAAA,KkBxRJ,YAAA,IACA,YAAA,IACA,MAAA,QACA,iBAAA,KACA,iBAAA,gOACA,kBAAA,UACA,oBAAA,MAAA,OAAA,OACA,gBAAA,KAAA,KACA,OAAA,IAAA,MAAA,QjBFE,cAAA,OeHE,WAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YESJ,mBAAA,KAAA,gBAAA,KAAA,WAAA,KFLI,uCEfN,aFgBQ,WAAA,MEMN,mBACE,aAAA,QACA,QAAA,EAKE,WAAA,EAAA,EAAA,EAAA,OAAA,qBAIJ,uBAAA,mCAEE,cAAA,OACA,iBAAA,KAGF,sBAEE,iBAAA,QAKF,4BACE,MAAA,YACA,YAAA,EAAA,EAAA,EAAA,QAIJ,gBACE,YAAA,OACA,eAAA,OACA,aAAA,MlByOI,UAAA,QkBrON,gBACE,YAAA,MACA,eAAA,MACA,aAAA,KlBkOI,UAAA,QmBjSN,YACE,QAAA,MACA,WAAA,OACA,aAAA,MACA,cAAA,QAEA,8BACE,MAAA,KACA,YAAA,OAIJ,kBACE,MAAA,IACA,OAAA,IACA,WAAA,MACA,eAAA,IACA,iBAAA,KACA,kBAAA,UACA,oBAAA,OACA,gBAAA,QACA,OAAA,IAAA,MAAA,gBACA,mBAAA,KAAA,gBAAA,KAAA,WAAA,KACA,2BAAA,MAAA,aAAA,MAGA,iClBXE,cAAA,MkBeF,8BAEE,cAAA,IAGF,yBACE,OAAA,gBAGF,wBACE,aAAA,QACA,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,OAAA,qBAGF,0BACE,iBAAA,QACA,aAAA,QAEA,yCAII,iBAAA,8NAIJ,sCAII,iBAAA,sIAKN,+CACE,iBAAA,QACA,aAAA,QAKE,iBAAA,wNAIJ,2BACE,eAAA,KACA,OAAA,KACA,QAAA,GAOA,6CAAA,8CACE,QAAA,GAcN,aACE,aAAA,MAEA,+BACE,MAAA,IACA,YAAA,OACA,iBAAA,uJACA,oBAAA,KAAA,OlB9FA,cAAA,IeHE,WAAA,oBAAA,KAAA,YAIA,uCGyFJ,+BHxFM,WAAA,MGgGJ,qCACE,iBAAA,yIAGF,uCACE,oBAAA,MAAA,OAKE,iBAAA,sIAMR,mBACE,QAAA,aACA,aAAA,KAGF,WACE,SAAA,SACA,KAAA,cACA,eAAA,KAIE,yBAAA,0BACE,eAAA,KACA,OAAA,KACA,QAAA,IC9IN,YACE,MAAA,KACA,OAAA,OACA,QAAA,EACA,iBAAA,YACA,mBAAA,KAAA,gBAAA,KAAA,WAAA,KAEA,kBACE,QAAA,EAIA,wCAA0B,WAAA,EAAA,EAAA,EAAA,IAAA,IAAA,CAAA,EAAA,EAAA,EAAA,OAAA,qBAC1B,oCAA0B,WAAA,EAAA,EAAA,EAAA,IAAA,IAAA,CAAA,EAAA,EAAA,EAAA,OAAA,qBAG5B,8BACE,OAAA,EAGF,kCACE,MAAA,KACA,OAAA,KACA,WAAA,QHzBF,iBAAA,QG2BE,OAAA,EnBZA,cAAA,KeHE,mBAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAAA,WAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YImBF,mBAAA,KAAA,WAAA,KJfE,uCIMJ,kCJLM,mBAAA,KAAA,WAAA,MIgBJ,yCHjCF,iBAAA,QGsCA,2CACE,MAAA,KACA,OAAA,MACA,MAAA,YACA,OAAA,QACA,iBAAA,QACA,aAAA,YnB7BA,cAAA,KmBkCF,8BACE,MAAA,KACA,OAAA,KHnDF,iBAAA,QGqDE,OAAA,EnBtCA,cAAA,KeHE,gBAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAAA,WAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YI6CF,gBAAA,KAAA,WAAA,KJzCE,uCIiCJ,8BJhCM,gBAAA,KAAA,WAAA,MI0CJ,qCH3DF,iBAAA,QGgEA,8BACE,MAAA,KACA,OAAA,MACA,MAAA,YACA,OAAA,QACA,iBAAA,QACA,aAAA,YnBvDA,cAAA,KmB4DF,qBACE,eAAA,KAEA,2CACE,iBAAA,QAGF,uCACE,iBAAA,QCvFN,eACE,SAAA,SAEA,6BtB+iFF,4BsB7iFI,OAAA,mBACA,YAAA,KAGF,qBACE,SAAA,SACA,IAAA,EACA,KAAA,EACA,OAAA,KACA,QAAA,KAAA,OACA,eAAA,KACA,OAAA,IAAA,MAAA,YACA,iBAAA,EAAA,ELDE,WAAA,QAAA,IAAA,WAAA,CAAA,UAAA,IAAA,YAIA,uCKXJ,qBLYM,WAAA,MKCN,6BACE,QAAA,KAAA,OAEA,+CACE,MAAA,YADF,0CACE,MAAA,YAGF,0DAEE,YAAA,SACA,eAAA,QAHF,mCAAA,qDAEE,YAAA,SACA,eAAA,QAGF,8CACE,YAAA,SACA,eAAA,QAIJ,4BACE,YAAA,SACA,eAAA,QAMA,gEACE,QAAA,IACA,UAAA,WAAA,mBAAA,mBAFF,yCtBmjFJ,2DACA,kCsBnjFM,QAAA,IACA,UAAA,WAAA,mBAAA,mBAKF,oDACE,QAAA,IACA,UAAA,WAAA,mBAAA,mBCtDN,aACE,SAAA,SACA,QAAA,KACA,UAAA,KACA,YAAA,QACA,MAAA,KAEA,2BvB2mFF,0BuBzmFI,SAAA,SACA,KAAA,EAAA,EAAA,KACA,MAAA,GACA,UAAA,EAIF,iCvBymFF,gCuBvmFI,QAAA,EAMF,kBACE,SAAA,SACA,QAAA,EAEA,wBACE,QAAA,EAWN,kBACE,QAAA,KACA,YAAA,OACA,QAAA,QAAA,OtBsPI,UAAA,KsBpPJ,YAAA,IACA,YAAA,IACA,MAAA,QACA,WAAA,OACA,YAAA,OACA,iBAAA,QACA,OAAA,IAAA,MAAA,QrBpCE,cAAA,OFuoFJ,qBuBzlFA,8BvBulFA,6BACA,kCuBplFE,QAAA,MAAA,KtBgOI,UAAA,QClRF,cAAA,MFgpFJ,qBuBzlFA,8BvBulFA,6BACA,kCuBplFE,QAAA,OAAA,MtBuNI,UAAA,QClRF,cAAA,MqBgEJ,6BvBulFA,6BuBrlFE,cAAA,KvB0lFF,uEuB7kFI,8FrB/DA,wBAAA,EACA,2BAAA,EFgpFJ,iEuB3kFI,2FrBtEA,wBAAA,EACA,2BAAA,EqBgFF,0IACE,YAAA,KrBpEA,uBAAA,EACA,0BAAA,EsBzBF,gBACE,QAAA,KACA,MAAA,KACA,WAAA,OvByQE,UAAA,OuBtQF,MAAA,QAGF,eACE,SAAA,SACA,IAAA,KACA,QAAA,EACA,QAAA,KACA,UAAA,KACA,QAAA,OAAA,MACA,WAAA,MvB4PE,UAAA,QuBzPF,MAAA,KACA,iBAAA,mBtB1BA,cAAA,OFmsFJ,0BACA,yBwBrqFI,sCxBmqFJ,qCwBjqFM,QAAA,MA9CF,uBAAA,mCAoDE,aAAA,QAGE,cAAA,qBACA,iBAAA,2OACA,kBAAA,UACA,oBAAA,MAAA,wBAAA,OACA,gBAAA,sBAAA,sBAGF,6BAAA,yCACE,aAAA,QACA,WAAA,EAAA,EAAA,EAAA,OAAA,oBAhEJ,2CAAA,+BAyEI,cAAA,qBACA,oBAAA,IAAA,wBAAA,MAAA,wBA1EJ,sBAAA,kCAiFE,aAAA,QAGE,kDAAA,gDAAA,8DAAA,4DAEE,cAAA,SACA,iBAAA,+NAAA,CAAA,2OACA,oBAAA,MAAA,OAAA,MAAA,CAAA,OAAA,MAAA,QACA,gBAAA,KAAA,IAAA,CAAA,sBAAA,sBAIJ,4BAAA,wCACE,aAAA,QACA,WAAA,EAAA,EAAA,EAAA,OAAA,oBA/FJ,2BAAA,uCAsGE,aAAA,QAEA,mCAAA,+CACE,iBAAA,QAGF,iCAAA,6CACE,WAAA,EAAA,EAAA,EAAA,OAAA,oBAGF,6CAAA,yDACE,MAAA,QAKJ,qDACE,YAAA,KAvHF,oCxBwwFJ,mCwBxwFI,gDxBuwFJ,+CwBxoFQ,QAAA,EAIF,0CxB0oFN,yCwB1oFM,sDxByoFN,qDwBxoFQ,QAAA,EAjHN,kBACE,QAAA,KACA,MAAA,KACA,WAAA,OvByQE,UAAA,OuBtQF,MAAA,QAGF,iBACE,SAAA,SACA,IAAA,KACA,QAAA,EACA,QAAA,KACA,UAAA,KACA,QAAA,OAAA,MACA,WAAA,MvB4PE,UAAA,QuBzPF,MAAA,KACA,iBAAA,mBtB1BA,cAAA,OF4xFJ,8BACA,6BwB9vFI,0CxB4vFJ,yCwB1vFM,QAAA,MA9CF,yBAAA,qCAoDE,aAAA,QAGE,cAAA,qBACA,iBAAA,2TACA,kBAAA,UACA,oBAAA,MAAA,wBAAA,OACA,gBAAA,sBAAA,sBAGF,+BAAA,2CACE,aAAA,QACA,WAAA,EAAA,EAAA,EAAA,OAAA,oBAhEJ,6CAAA,iCAyEI,cAAA,qBACA,oBAAA,IAAA,wBAAA,MAAA,wBA1EJ,wBAAA,oCAiFE,aAAA,QAGE,oDAAA,kDAAA,gEAAA,8DAEE,cAAA,SACA,iBAAA,+NAAA,CAAA,2TACA,oBAAA,MAAA,OAAA,MAAA,CAAA,OAAA,MAAA,QACA,gBAAA,KAAA,IAAA,CAAA,sBAAA,sBAIJ,8BAAA,0CACE,aAAA,QACA,WAAA,EAAA,EAAA,EAAA,OAAA,oBA/FJ,6BAAA,yCAsGE,aAAA,QAEA,qCAAA,iDACE,iBAAA,QAGF,mCAAA,+CACE,WAAA,EAAA,EAAA,EAAA,OAAA,oBAGF,+CAAA,2DACE,MAAA,QAKJ,uDACE,YAAA,KAvHF,sCxBi2FJ,qCwBj2FI,kDxBg2FJ,iDwB/tFQ,QAAA,EAEF,4CxBmuFN,2CwBnuFM,wDxBkuFN,uDwBjuFQ,QAAA,ECtIR,KACE,QAAA,aAEA,YAAA,IACA,YAAA,IACA,MAAA,QACA,WAAA,OACA,gBAAA,KAEA,eAAA,OACA,OAAA,QACA,oBAAA,KAAA,iBAAA,KAAA,YAAA,KACA,iBAAA,YACA,OAAA,IAAA,MAAA,YC8GA,QAAA,QAAA,OzBsKI,UAAA,KClRF,cAAA,OeHE,WAAA,MAAA,KAAA,WAAA,CAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAIA,uCQhBN,KRiBQ,WAAA,MQAN,WACE,MAAA,QAIF,sBAAA,WAEE,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,OAAA,qBAcF,cAAA,cAAA,uBAGE,eAAA,KACA,QAAA,IAYF,aCvCA,MAAA,KRhBA,iBAAA,QQkBA,aAAA,QAGA,mBACE,MAAA,KRtBF,iBAAA,QQwBE,aAAA,QAGF,8BAAA,mBAEE,MAAA,KR7BF,iBAAA,QQ+BE,aAAA,QAKE,WAAA,EAAA,EAAA,EAAA,OAAA,oBAIJ,+BAAA,gCAAA,oBAAA,oBAAA,mCAKE,MAAA,KACA,iBAAA,QAGA,aAAA,QAEA,qCAAA,sCAAA,0BAAA,0BAAA,yCAKI,WAAA,EAAA,EAAA,EAAA,OAAA,oBAKN,sBAAA,sBAEE,MAAA,KACA,iBAAA,QAGA,aAAA,QDZF,eCvCA,MAAA,KRhBA,iBAAA,QQkBA,aAAA,QAGA,qBACE,MAAA,KRtBF,iBAAA,QQwBE,aAAA,QAGF,gCAAA,qBAEE,MAAA,KR7BF,iBAAA,QQ+BE,aAAA,QAKE,WAAA,EAAA,EAAA,EAAA,OAAA,qBAIJ,iCAAA,kCAAA,sBAAA,sBAAA,qCAKE,MAAA,KACA,iBAAA,QAGA,aAAA,QAEA,uCAAA,wCAAA,4BAAA,4BAAA,2CAKI,WAAA,EAAA,EAAA,EAAA,OAAA,qBAKN,wBAAA,wBAEE,MAAA,KACA,iBAAA,QAGA,aAAA,QDZF,aCvCA,MAAA,KRhBA,iBAAA,QQkBA,aAAA,QAGA,mBACE,MAAA,KRtBF,iBAAA,QQwBE,aAAA,QAGF,8BAAA,mBAEE,MAAA,KR7BF,iBAAA,QQ+BE,aAAA,QAKE,WAAA,EAAA,EAAA,EAAA,OAAA,oBAIJ,+BAAA,gCAAA,oBAAA,oBAAA,mCAKE,MAAA,KACA,iBAAA,QAGA,aAAA,QAEA,qCAAA,sCAAA,0BAAA,0BAAA,yCAKI,WAAA,EAAA,EAAA,EAAA,OAAA,oBAKN,sBAAA,sBAEE,MAAA,KACA,iBAAA,QAGA,aAAA,QDZF,UCvCA,MAAA,KRhBA,iBAAA,QQkBA,aAAA,QAGA,gBACE,MAAA,KRtBF,iBAAA,QQwBE,aAAA,QAGF,2BAAA,gBAEE,MAAA,KR7BF,iBAAA,QQ+BE,aAAA,QAKE,WAAA,EAAA,EAAA,EAAA,OAAA,oBAIJ,4BAAA,6BAAA,iBAAA,iBAAA,gCAKE,MAAA,KACA,iBAAA,QAGA,aAAA,QAEA,kCAAA,mCAAA,uBAAA,uBAAA,sCAKI,WAAA,EAAA,EAAA,EAAA,OAAA,oBAKN,mBAAA,mBAEE,MAAA,KACA,iBAAA,QAGA,aAAA,QDZF,aCvCA,MAAA,KRhBA,iBAAA,QQkBA,aAAA,QAGA,mBACE,MAAA,KRtBF,iBAAA,QQwBE,aAAA,QAGF,8BAAA,mBAEE,MAAA,KR7BF,iBAAA,QQ+BE,aAAA,QAKE,WAAA,EAAA,EAAA,EAAA,OAAA,mBAIJ,+BAAA,gCAAA,oBAAA,oBAAA,mCAKE,MAAA,KACA,iBAAA,QAGA,aAAA,QAEA,qCAAA,sCAAA,0BAAA,0BAAA,yCAKI,WAAA,EAAA,EAAA,EAAA,OAAA,mBAKN,sBAAA,sBAEE,MAAA,KACA,iBAAA,QAGA,aAAA,QDZF,YCvCA,MAAA,KRhBA,iBAAA,QQkBA,aAAA,QAGA,kBACE,MAAA,KRtBF,iBAAA,QQwBE,aAAA,QAGF,6BAAA,kBAEE,MAAA,KR7BF,iBAAA,QQ+BE,aAAA,QAKE,WAAA,EAAA,EAAA,EAAA,OAAA,mBAIJ,8BAAA,+BAAA,mBAAA,mBAAA,kCAKE,MAAA,KACA,iBAAA,QAGA,aAAA,QAEA,oCAAA,qCAAA,yBAAA,yBAAA,wCAKI,WAAA,EAAA,EAAA,EAAA,OAAA,mBAKN,qBAAA,qBAEE,MAAA,KACA,iBAAA,QAGA,aAAA,QDZF,WCvCA,MAAA,KRhBA,iBAAA,QQkBA,aAAA,QAGA,iBACE,MAAA,KRtBF,iBAAA,QQwBE,aAAA,QAGF,4BAAA,iBAEE,MAAA,KR7BF,iBAAA,QQ+BE,aAAA,QAKE,WAAA,EAAA,EAAA,EAAA,OAAA,qBAIJ,6BAAA,8BAAA,kBAAA,kBAAA,iCAKE,MAAA,KACA,iBAAA,QAGA,aAAA,QAEA,mCAAA,oCAAA,wBAAA,wBAAA,uCAKI,WAAA,EAAA,EAAA,EAAA,OAAA,qBAKN,oBAAA,oBAEE,MAAA,KACA,iBAAA,QAGA,aAAA,QDZF,UCvCA,MAAA,KRhBA,iBAAA,QQkBA,aAAA,QAGA,gBACE,MAAA,KRtBF,iBAAA,QQwBE,aAAA,QAGF,2BAAA,gBAEE,MAAA,KR7BF,iBAAA,QQ+BE,aAAA,QAKE,WAAA,EAAA,EAAA,EAAA,OAAA,kBAIJ,4BAAA,6BAAA,iBAAA,iBAAA,gCAKE,MAAA,KACA,iBAAA,QAGA,aAAA,QAEA,kCAAA,mCAAA,uBAAA,uBAAA,sCAKI,WAAA,EAAA,EAAA,EAAA,OAAA,kBAKN,mBAAA,mBAEE,MAAA,KACA,iBAAA,QAGA,aAAA,QDNF,qBCmBA,MAAA,QACA,aAAA,QAEA,2BACE,MAAA,KACA,iBAAA,QACA,aAAA,QAGF,sCAAA,2BAEE,WAAA,EAAA,EAAA,EAAA,OAAA,oBAGF,uCAAA,wCAAA,4BAAA,0CAAA,4BAKE,MAAA,KACA,iBAAA,QACA,aAAA,QAEA,6CAAA,8CAAA,kCAAA,gDAAA,kCAKI,WAAA,EAAA,EAAA,EAAA,OAAA,oBAKN,8BAAA,8BAEE,MAAA,QACA,iBAAA,YDvDF,uBCmBA,MAAA,QACA,aAAA,QAEA,6BACE,MAAA,KACA,iBAAA,QACA,aAAA,QAGF,wCAAA,6BAEE,WAAA,EAAA,EAAA,EAAA,OAAA,qBAGF,yCAAA,0CAAA,8BAAA,4CAAA,8BAKE,MAAA,KACA,iBAAA,QACA,aAAA,QAEA,+CAAA,gDAAA,oCAAA,kDAAA,oCAKI,WAAA,EAAA,EAAA,EAAA,OAAA,qBAKN,gCAAA,gCAEE,MAAA,QACA,iBAAA,YDvDF,qBCmBA,MAAA,QACA,aAAA,QAEA,2BACE,MAAA,KACA,iBAAA,QACA,aAAA,QAGF,sCAAA,2BAEE,WAAA,EAAA,EAAA,EAAA,OAAA,mBAGF,uCAAA,wCAAA,4BAAA,0CAAA,4BAKE,MAAA,KACA,iBAAA,QACA,aAAA,QAEA,6CAAA,8CAAA,kCAAA,gDAAA,kCAKI,WAAA,EAAA,EAAA,EAAA,OAAA,mBAKN,8BAAA,8BAEE,MAAA,QACA,iBAAA,YDvDF,kBCmBA,MAAA,QACA,aAAA,QAEA,wBACE,MAAA,KACA,iBAAA,QACA,aAAA,QAGF,mCAAA,wBAEE,WAAA,EAAA,EAAA,EAAA,OAAA,oBAGF,oCAAA,qCAAA,yBAAA,uCAAA,yBAKE,MAAA,KACA,iBAAA,QACA,aAAA,QAEA,0CAAA,2CAAA,+BAAA,6CAAA,+BAKI,WAAA,EAAA,EAAA,EAAA,OAAA,oBAKN,2BAAA,2BAEE,MAAA,QACA,iBAAA,YDvDF,qBCmBA,MAAA,QACA,aAAA,QAEA,2BACE,MAAA,KACA,iBAAA,QACA,aAAA,QAGF,sCAAA,2BAEE,WAAA,EAAA,EAAA,EAAA,OAAA,mBAGF,uCAAA,wCAAA,4BAAA,0CAAA,4BAKE,MAAA,KACA,iBAAA,QACA,aAAA,QAEA,6CAAA,8CAAA,kCAAA,gDAAA,kCAKI,WAAA,EAAA,EAAA,EAAA,OAAA,mBAKN,8BAAA,8BAEE,MAAA,QACA,iBAAA,YDvDF,oBCmBA,MAAA,QACA,aAAA,QAEA,0BACE,MAAA,KACA,iBAAA,QACA,aAAA,QAGF,qCAAA,0BAEE,WAAA,EAAA,EAAA,EAAA,OAAA,mBAGF,sCAAA,uCAAA,2BAAA,yCAAA,2BAKE,MAAA,KACA,iBAAA,QACA,aAAA,QAEA,4CAAA,6CAAA,iCAAA,+CAAA,iCAKI,WAAA,EAAA,EAAA,EAAA,OAAA,mBAKN,6BAAA,6BAEE,MAAA,QACA,iBAAA,YDvDF,mBCmBA,MAAA,QACA,aAAA,QAEA,yBACE,MAAA,KACA,iBAAA,QACA,aAAA,QAGF,oCAAA,yBAEE,WAAA,EAAA,EAAA,EAAA,OAAA,qBAGF,qCAAA,sCAAA,0BAAA,wCAAA,0BAKE,MAAA,KACA,iBAAA,QACA,aAAA,QAEA,2CAAA,4CAAA,gCAAA,8CAAA,gCAKI,WAAA,EAAA,EAAA,EAAA,OAAA,qBAKN,4BAAA,4BAEE,MAAA,QACA,iBAAA,YDvDF,kBCmBA,MAAA,QACA,aAAA,QAEA,wBACE,MAAA,KACA,iBAAA,QACA,aAAA,QAGF,mCAAA,wBAEE,WAAA,EAAA,EAAA,EAAA,OAAA,kBAGF,oCAAA,qCAAA,yBAAA,uCAAA,yBAKE,MAAA,KACA,iBAAA,QACA,aAAA,QAEA,0CAAA,2CAAA,+BAAA,6CAAA,+BAKI,WAAA,EAAA,EAAA,EAAA,OAAA,kBAKN,2BAAA,2BAEE,MAAA,QACA,iBAAA,YD3CJ,UACE,YAAA,IACA,MAAA,QACA,gBAAA,UAEA,gBACE,MAAA,QAQF,mBAAA,mBAEE,MAAA,QAWJ,mBAAA,QCuBE,QAAA,MAAA,KzBsKI,UAAA,QClRF,cAAA,MuByFJ,mBAAA,QCmBE,QAAA,OAAA,MzBsKI,UAAA,QClRF,cAAA,MyBnBJ,MVgBM,WAAA,QAAA,KAAA,OAIA,uCUpBN,MVqBQ,WAAA,MUlBN,iBACE,QAAA,EAMF,qBACE,QAAA,KAIJ,YACE,OAAA,EACA,SAAA,OVDI,WAAA,OAAA,KAAA,KAIA,uCULN,YVMQ,WAAA,MUDN,gCACE,MAAA,EACA,OAAA,KVNE,WAAA,MAAA,KAAA,KAIA,uCUAJ,gCVCM,WAAA,MjBs3GR,UADA,SAEA,W4B34GA,QAIE,SAAA,SAGF,iBACE,YAAA,OCqBE,wBACE,QAAA,aACA,YAAA,OACA,eAAA,OACA,QAAA,GAhCJ,WAAA,KAAA,MACA,aAAA,KAAA,MAAA,YACA,cAAA,EACA,YAAA,KAAA,MAAA,YAqDE,8BACE,YAAA,ED3CN,eACE,SAAA,SACA,QAAA,KACA,QAAA,KACA,UAAA,MACA,QAAA,MAAA,EACA,OAAA,E3B+QI,UAAA,K2B7QJ,MAAA,QACA,WAAA,KACA,WAAA,KACA,iBAAA,KACA,gBAAA,YACA,OAAA,IAAA,MAAA,gB1BVE,cAAA,O0BcF,+BACE,IAAA,KACA,KAAA,EACA,WAAA,QAYA,qBACE,cAAA,MAEA,qCACE,MAAA,KACA,KAAA,EAIJ,mBACE,cAAA,IAEA,mCACE,MAAA,EACA,KAAA,KnBCJ,yBmBfA,wBACE,cAAA,MAEA,wCACE,MAAA,KACA,KAAA,EAIJ,sBACE,cAAA,IAEA,sCACE,MAAA,EACA,KAAA,MnBCJ,yBmBfA,wBACE,cAAA,MAEA,wCACE,MAAA,KACA,KAAA,EAIJ,sBACE,cAAA,IAEA,sCACE,MAAA,EACA,KAAA,MnBCJ,yBmBfA,wBACE,cAAA,MAEA,wCACE,MAAA,KACA,KAAA,EAIJ,sBACE,cAAA,IAEA,sCACE,MAAA,EACA,KAAA,MnBCJ,0BmBfA,wBACE,cAAA,MAEA,wCACE,MAAA,KACA,KAAA,EAIJ,sBACE,cAAA,IAEA,sCACE,MAAA,EACA,KAAA,MnBCJ,0BmBfA,yBACE,cAAA,MAEA,yCACE,MAAA,KACA,KAAA,EAIJ,uBACE,cAAA,IAEA,uCACE,MAAA,EACA,KAAA,MAUN,uCACE,IAAA,KACA,OAAA,KACA,WAAA,EACA,cAAA,QC9CA,gCACE,QAAA,aACA,YAAA,OACA,eAAA,OACA,QAAA,GAzBJ,WAAA,EACA,aAAA,KAAA,MAAA,YACA,cAAA,KAAA,MACA,YAAA,KAAA,MAAA,YA8CE,sCACE,YAAA,ED0BJ,wCACE,IAAA,EACA,MAAA,KACA,KAAA,KACA,WAAA,EACA,YAAA,QC5DA,iCACE,QAAA,aACA,YAAA,OACA,eAAA,OACA,QAAA,GAlBJ,WAAA,KAAA,MAAA,YACA,aAAA,EACA,cAAA,KAAA,MAAA,YACA,YAAA,KAAA,MAuCE,uCACE,YAAA,EDoCF,iCACE,eAAA,EAMJ,0CACE,IAAA,EACA,MAAA,KACA,KAAA,KACA,WAAA,EACA,aAAA,QC7EA,mCACE,QAAA,aACA,YAAA,OACA,eAAA,OACA,QAAA,GAWA,mCACE,QAAA,KAGF,oCACE,QAAA,aACA,aAAA,OACA,eAAA,OACA,QAAA,GA9BN,WAAA,KAAA,MAAA,YACA,aAAA,KAAA,MACA,cAAA,KAAA,MAAA,YAiCE,yCACE,YAAA,EDqDF,oCACE,eAAA,EAON,kBACE,OAAA,EACA,OAAA,MAAA,EACA,SAAA,OACA,WAAA,IAAA,MAAA,gBAMF,eACE,QAAA,MACA,MAAA,KACA,QAAA,OAAA,KACA,MAAA,KACA,YAAA,IACA,MAAA,QACA,WAAA,QACA,gBAAA,KACA,YAAA,OACA,iBAAA,YACA,OAAA,EAcA,qBAAA,qBAEE,MAAA,QVzJF,iBAAA,QU8JA,sBAAA,sBAEE,MAAA,KACA,gBAAA,KVjKF,iBAAA,QUqKA,wBAAA,wBAEE,MAAA,QACA,eAAA,KACA,iBAAA,YAMJ,oBACE,QAAA,MAIF,iBACE,QAAA,MACA,QAAA,MAAA,KACA,cAAA,E3B0GI,UAAA,Q2BxGJ,MAAA,QACA,YAAA,OAIF,oBACE,QAAA,MACA,QAAA,OAAA,KACA,MAAA,QAIF,oBACE,MAAA,QACA,iBAAA,QACA,aAAA,gBAGA,mCACE,MAAA,QAEA,yCAAA,yCAEE,MAAA,KVhNJ,iBAAA,sBUoNE,0CAAA,0CAEE,MAAA,KVtNJ,iBAAA,QU0NE,4CAAA,4CAEE,MAAA,QAIJ,sCACE,aAAA,gBAGF,wCACE,MAAA,QAGF,qCACE,MAAA,QE5OJ,W9B2rHA,oB8BzrHE,SAAA,SACA,QAAA,YACA,eAAA,O9B6rHF,yB8B3rHE,gBACE,SAAA,SACA,KAAA,EAAA,EAAA,K9BmsHJ,4CACA,0CAIA,gCADA,gCADA,+BADA,+B8BhsHE,mC9ByrHF,iCAIA,uBADA,uBADA,sBADA,sB8BprHI,QAAA,EAKJ,aACE,QAAA,KACA,UAAA,KACA,gBAAA,WAEA,0BACE,MAAA,K9BgsHJ,wC8B1rHE,kCAEE,YAAA,K9B4rHJ,4C8BxrHE,uD5BRE,wBAAA,EACA,2BAAA,EFqsHJ,6C8BrrHE,+B9BorHF,iCEvrHI,uBAAA,EACA,0BAAA,E4BqBJ,uBACE,cAAA,SACA,aAAA,SAEA,8BAAA,uCAAA,sCAGE,YAAA,EAGF,0CACE,aAAA,EAIJ,0CAAA,+BACE,cAAA,QACA,aAAA,QAGF,0CAAA,+BACE,cAAA,OACA,aAAA,OAoBF,oBACE,eAAA,OACA,YAAA,WACA,gBAAA,OAEA,yB9BmpHF,+B8BjpHI,MAAA,K9BqpHJ,iD8BlpHE,2CAEE,WAAA,K9BopHJ,qD8BhpHE,gE5BvFE,2BAAA,EACA,0BAAA,EF2uHJ,sD8BhpHE,8B5B1GE,uBAAA,EACA,wBAAA,E6BxBJ,KACE,QAAA,KACA,UAAA,KACA,aAAA,EACA,cAAA,EACA,WAAA,KAGF,UACE,QAAA,MACA,QAAA,MAAA,KAGA,MAAA,QACA,gBAAA,KdHI,WAAA,MAAA,KAAA,WAAA,CAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,YAIA,uCcPN,UdQQ,WAAA,McCN,gBAAA,gBAEE,MAAA,QAKF,mBACE,MAAA,QACA,eAAA,KACA,OAAA,QAQJ,UACE,cAAA,IAAA,MAAA,QAEA,oBACE,cAAA,KACA,WAAA,IACA,OAAA,IAAA,MAAA,Y7BlBA,uBAAA,OACA,wBAAA,O6BoBA,0BAAA,0BAEE,aAAA,QAAA,QAAA,QAEA,UAAA,QAGF,6BACE,MAAA,QACA,iBAAA,YACA,aAAA,Y/BixHN,mC+B7wHE,2BAEE,MAAA,QACA,iBAAA,KACA,aAAA,QAAA,QAAA,KAGF,yBAEE,WAAA,K7B5CA,uBAAA,EACA,wBAAA,E6BuDF,qBACE,WAAA,IACA,OAAA,E7BnEA,cAAA,O6BuEF,4B/BmwHF,2B+BjwHI,MAAA,KbxFF,iBAAA,QlB+1HF,oB+B5vHE,oBAEE,KAAA,EAAA,EAAA,KACA,WAAA,O/B+vHJ,yB+B1vHE,yBAEE,WAAA,EACA,UAAA,EACA,WAAA,OAMF,8B/BuvHF,mC+BtvHI,MAAA,KAUF,uBACE,QAAA,KAEF,qBACE,QAAA,MCxHJ,QACE,SAAA,SACA,QAAA,KACA,UAAA,KACA,YAAA,OACA,gBAAA,cACA,YAAA,MAEA,eAAA,MAOA,mBhCs2HF,yBAGA,sBADA,sBADA,sBAGA,sBACA,uBgC12HI,QAAA,KACA,UAAA,QACA,YAAA,OACA,gBAAA,cAoBJ,cACE,YAAA,SACA,eAAA,SACA,aAAA,K/B2OI,UAAA,Q+BzOJ,gBAAA,KACA,YAAA,OAaF,YACE,QAAA,KACA,eAAA,OACA,aAAA,EACA,cAAA,EACA,WAAA,KAEA,sBACE,cAAA,EACA,aAAA,EAGF,2BACE,SAAA,OASJ,aACE,YAAA,MACA,eAAA,MAYF,iBACE,WAAA,KACA,UAAA,EAGA,YAAA,OAIF,gBACE,QAAA,OAAA,O/B6KI,UAAA,Q+B3KJ,YAAA,EACA,iBAAA,YACA,OAAA,IAAA,MAAA,Y9BzGE,cAAA,OeHE,WAAA,WAAA,KAAA,YAIA,uCemGN,gBflGQ,WAAA,Me2GN,sBACE,gBAAA,KAGF,sBACE,gBAAA,KACA,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,OAMJ,qBACE,QAAA,aACA,MAAA,MACA,OAAA,MACA,eAAA,OACA,kBAAA,UACA,oBAAA,OACA,gBAAA,KAGF,mBACE,WAAA,6BACA,WAAA,KvB1FE,yBuBsGA,kBAEI,UAAA,OACA,gBAAA,WAEA,8BACE,eAAA,IAEA,6CACE,SAAA,SAGF,wCACE,cAAA,MACA,aAAA,MAIJ,qCACE,SAAA,QAGF,mCACE,QAAA,eACA,WAAA,KAGF,kCACE,QAAA,KAGF,oCACE,QAAA,KAGF,6BACE,SAAA,QACA,OAAA,EACA,QAAA,KACA,UAAA,EACA,WAAA,kBACA,iBAAA,YACA,aAAA,EACA,YAAA,EfhMJ,WAAA,KekMI,UAAA,KhC+yHV,oCgC7yHQ,iCAEE,OAAA,KACA,WAAA,EACA,cAAA,EAGF,kCACE,QAAA,KACA,UAAA,EACA,QAAA,EACA,WAAA,SvBhKN,yBuBsGA,kBAEI,UAAA,OACA,gBAAA,WAEA,8BACE,eAAA,IAEA,6CACE,SAAA,SAGF,wCACE,cAAA,MACA,aAAA,MAIJ,qCACE,SAAA,QAGF,mCACE,QAAA,eACA,WAAA,KAGF,kCACE,QAAA,KAGF,oCACE,QAAA,KAGF,6BACE,SAAA,QACA,OAAA,EACA,QAAA,KACA,UAAA,EACA,WAAA,kBACA,iBAAA,YACA,aAAA,EACA,YAAA,EfhMJ,WAAA,KekMI,UAAA,KhCo2HV,oCgCl2HQ,iCAEE,OAAA,KACA,WAAA,EACA,cAAA,EAGF,kCACE,QAAA,KACA,UAAA,EACA,QAAA,EACA,WAAA,SvBhKN,yBuBsGA,kBAEI,UAAA,OACA,gBAAA,WAEA,8BACE,eAAA,IAEA,6CACE,SAAA,SAGF,wCACE,cAAA,MACA,aAAA,MAIJ,qCACE,SAAA,QAGF,mCACE,QAAA,eACA,WAAA,KAGF,kCACE,QAAA,KAGF,oCACE,QAAA,KAGF,6BACE,SAAA,QACA,OAAA,EACA,QAAA,KACA,UAAA,EACA,WAAA,kBACA,iBAAA,YACA,aAAA,EACA,YAAA,EfhMJ,WAAA,KekMI,UAAA,KhCy5HV,oCgCv5HQ,iCAEE,OAAA,KACA,WAAA,EACA,cAAA,EAGF,kCACE,QAAA,KACA,UAAA,EACA,QAAA,EACA,WAAA,SvBhKN,0BuBsGA,kBAEI,UAAA,OACA,gBAAA,WAEA,8BACE,eAAA,IAEA,6CACE,SAAA,SAGF,wCACE,cAAA,MACA,aAAA,MAIJ,qCACE,SAAA,QAGF,mCACE,QAAA,eACA,WAAA,KAGF,kCACE,QAAA,KAGF,oCACE,QAAA,KAGF,6BACE,SAAA,QACA,OAAA,EACA,QAAA,KACA,UAAA,EACA,WAAA,kBACA,iBAAA,YACA,aAAA,EACA,YAAA,EfhMJ,WAAA,KekMI,UAAA,KhC88HV,oCgC58HQ,iCAEE,OAAA,KACA,WAAA,EACA,cAAA,EAGF,kCACE,QAAA,KACA,UAAA,EACA,QAAA,EACA,WAAA,SvBhKN,0BuBsGA,mBAEI,UAAA,OACA,gBAAA,WAEA,+BACE,eAAA,IAEA,8CACE,SAAA,SAGF,yCACE,cAAA,MACA,aAAA,MAIJ,sCACE,SAAA,QAGF,oCACE,QAAA,eACA,WAAA,KAGF,mCACE,QAAA,KAGF,qCACE,QAAA,KAGF,8BACE,SAAA,QACA,OAAA,EACA,QAAA,KACA,UAAA,EACA,WAAA,kBACA,iBAAA,YACA,aAAA,EACA,YAAA,EfhMJ,WAAA,KekMI,UAAA,KhCmgIV,qCgCjgIQ,kCAEE,OAAA,KACA,WAAA,EACA,cAAA,EAGF,mCACE,QAAA,KACA,UAAA,EACA,QAAA,EACA,WAAA,SA1DN,eAEI,UAAA,OACA,gBAAA,WAEA,2BACE,eAAA,IAEA,0CACE,SAAA,SAGF,qCACE,cAAA,MACA,aAAA,MAIJ,kCACE,SAAA,QAGF,gCACE,QAAA,eACA,WAAA,KAGF,+BACE,QAAA,KAGF,iCACE,QAAA,KAGF,0BACE,SAAA,QACA,OAAA,EACA,QAAA,KACA,UAAA,EACA,WAAA,kBACA,iBAAA,YACA,aAAA,EACA,YAAA,EfhMJ,WAAA,KekMI,UAAA,KhCujIV,iCgCrjIQ,8BAEE,OAAA,KACA,WAAA,EACA,cAAA,EAGF,+BACE,QAAA,KACA,UAAA,EACA,QAAA,EACA,WAAA,QAcR,4BACE,MAAA,eAEA,kCAAA,kCAEE,MAAA,eAKF,oCACE,MAAA,gBAEA,0CAAA,0CAEE,MAAA,eAGF,6CACE,MAAA,ehCqiIR,2CgCjiII,0CAEE,MAAA,eAIJ,8BACE,MAAA,gBACA,aAAA,eAGF,mCACE,iBAAA,4OAGF,2BACE,MAAA,gBAEA,6BhC8hIJ,mCADA,mCgC1hIM,MAAA,eAOJ,2BACE,MAAA,KAEA,iCAAA,iCAEE,MAAA,KAKF,mCACE,MAAA,sBAEA,yCAAA,yCAEE,MAAA,sBAGF,4CACE,MAAA,sBhCqhIR,0CgCjhII,yCAEE,MAAA,KAIJ,6BACE,MAAA,sBACA,aAAA,qBAGF,kCACE,iBAAA,kPAGF,0BACE,MAAA,sBACA,4BhC+gIJ,kCADA,kCgC3gIM,MAAA,KCvUN,MACE,SAAA,SACA,QAAA,KACA,eAAA,OACA,UAAA,EAEA,UAAA,WACA,iBAAA,KACA,gBAAA,WACA,OAAA,IAAA,MAAA,iB/BME,cAAA,O+BFF,SACE,aAAA,EACA,YAAA,EAGF,kBACE,WAAA,QACA,cAAA,QAEA,8BACE,iBAAA,E/BCF,uBAAA,mBACA,wBAAA,mB+BEA,6BACE,oBAAA,E/BUF,2BAAA,mBACA,0BAAA,mB+BJF,+BjCk1IF,+BiCh1II,WAAA,EAIJ,WAGE,KAAA,EAAA,EAAA,KACA,QAAA,KAAA,KAIF,YACE,cAAA,MAGF,eACE,WAAA,QACA,cAAA,EAGF,sBACE,cAAA,EAQA,sBACE,YAAA,KAQJ,aACE,QAAA,MAAA,KACA,cAAA,EAEA,iBAAA,gBACA,cAAA,IAAA,MAAA,iBAEA,yB/BpEE,cAAA,mBAAA,mBAAA,EAAA,E+ByEJ,aACE,QAAA,MAAA,KAEA,iBAAA,gBACA,WAAA,IAAA,MAAA,iBAEA,wB/B/EE,cAAA,EAAA,EAAA,mBAAA,mB+ByFJ,kBACE,aAAA,OACA,cAAA,OACA,YAAA,OACA,cAAA,EAUF,mBACE,aAAA,OACA,YAAA,OAIF,kBACE,SAAA,SACA,IAAA,EACA,MAAA,EACA,OAAA,EACA,KAAA,EACA,QAAA,K/BnHE,cAAA,mB+BuHJ,UjCozIA,iBADA,ciChzIE,MAAA,KAGF,UjCmzIA,cEv6II,uBAAA,mBACA,wBAAA,mB+BwHJ,UjCozIA,iBE/5II,2BAAA,mBACA,0BAAA,mB+BuHF,kBACE,cAAA,OxBpGA,yBwBgGJ,YAQI,QAAA,KACA,UAAA,IAAA,KAGA,kBAEE,KAAA,EAAA,EAAA,GACA,cAAA,EAEA,wBACE,YAAA,EACA,YAAA,EAKA,mC/BpJJ,wBAAA,EACA,2BAAA,EF+7IJ,gDiCzyIU,iDAGE,wBAAA,EjC0yIZ,gDiCxyIU,oDAGE,2BAAA,EAIJ,oC/BrJJ,uBAAA,EACA,0BAAA,EF67IJ,iDiCtyIU,kDAGE,uBAAA,EjCuyIZ,iDiCryIU,qDAGE,0BAAA,GC7MZ,kBACE,SAAA,SACA,QAAA,KACA,YAAA,OACA,MAAA,KACA,QAAA,KAAA,QjC4RI,UAAA,KiC1RJ,MAAA,QACA,WAAA,KACA,iBAAA,KACA,OAAA,EhCKE,cAAA,EgCHF,gBAAA,KjBAI,WAAA,MAAA,KAAA,WAAA,CAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,WAAA,CAAA,cAAA,KAAA,KAIA,uCiBhBN,kBjBiBQ,WAAA,MiBFN,kCACE,MAAA,QACA,iBAAA,QACA,WAAA,MAAA,EAAA,KAAA,EAAA,iBAEA,yCACE,iBAAA,gRACA,UAAA,gBAKJ,yBACE,YAAA,EACA,MAAA,QACA,OAAA,QACA,YAAA,KACA,QAAA,GACA,iBAAA,gRACA,kBAAA,UACA,gBAAA,QjBvBE,WAAA,UAAA,IAAA,YAIA,uCiBWJ,yBjBVM,WAAA,MiBsBN,wBACE,QAAA,EAGF,wBACE,QAAA,EACA,aAAA,QACA,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,OAAA,qBAIJ,kBACE,cAAA,EAGF,gBACE,iBAAA,KACA,OAAA,IAAA,MAAA,iBAEA,8BhCnCE,uBAAA,OACA,wBAAA,OgCqCA,gDhCtCA,uBAAA,mBACA,wBAAA,mBgC0CF,oCACE,WAAA,EAIF,6BhClCE,2BAAA,OACA,0BAAA,OgCqCE,yDhCtCF,2BAAA,mBACA,0BAAA,mBgC0CA,iDhC3CA,2BAAA,OACA,0BAAA,OgCgDJ,gBACE,QAAA,KAAA,QASA,qCACE,aAAA,EAGF,iCACE,aAAA,EACA,YAAA,EhCxFA,cAAA,EgC2FA,6CAAgB,WAAA,EAChB,4CAAe,cAAA,EAEf,mDhC9FA,cAAA,EiCnBJ,YACE,QAAA,KACA,UAAA,KACA,QAAA,EAAA,EACA,cAAA,KAEA,WAAA,KAOA,kCACE,aAAA,MAEA,0CACE,MAAA,KACA,cAAA,MACA,MAAA,QACA,QAAA,kCAIJ,wBACE,MAAA,QCzBJ,YACE,QAAA,KhCGA,aAAA,EACA,WAAA,KgCAF,WACE,SAAA,SACA,QAAA,MACA,MAAA,QACA,gBAAA,KACA,iBAAA,KACA,OAAA,IAAA,MAAA,QnBKI,WAAA,MAAA,KAAA,WAAA,CAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAIA,uCmBfN,WnBgBQ,WAAA,MmBPN,iBACE,QAAA,EACA,MAAA,QAEA,iBAAA,QACA,aAAA,QAGF,iBACE,QAAA,EACA,MAAA,QACA,iBAAA,QACA,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,OAAA,qBAKF,wCACE,YAAA,KAGF,6BACE,QAAA,EACA,MAAA,KlBlCF,iBAAA,QkBoCE,aAAA,QAGF,+BACE,MAAA,QACA,eAAA,KACA,iBAAA,KACA,aAAA,QC3CF,WACE,QAAA,QAAA,OAOI,kCnCqCJ,uBAAA,OACA,0BAAA,OmChCI,iCnCiBJ,wBAAA,OACA,2BAAA,OmChCF,0BACE,QAAA,OAAA,OpCgSE,UAAA,QoCzRE,iDnCqCJ,uBAAA,MACA,0BAAA,MmChCI,gDnCiBJ,wBAAA,MACA,2BAAA,MmChCF,0BACE,QAAA,OAAA,MpCgSE,UAAA,QoCzRE,iDnCqCJ,uBAAA,MACA,0BAAA,MmChCI,gDnCiBJ,wBAAA,MACA,2BAAA,MoC/BJ,OACE,QAAA,aACA,QAAA,MAAA,MrC8RI,UAAA,MqC5RJ,YAAA,IACA,YAAA,EACA,MAAA,KACA,WAAA,OACA,YAAA,OACA,eAAA,SpCKE,cAAA,OoCAF,aACE,QAAA,KAKJ,YACE,SAAA,SACA,IAAA,KCvBF,OACE,SAAA,SACA,QAAA,KAAA,KACA,cAAA,KACA,OAAA,IAAA,MAAA,YrCWE,cAAA,OqCNJ,eAEE,MAAA,QAIF,YACE,YAAA,IAQF,mBACE,cAAA,KAGA,8BACE,SAAA,SACA,IAAA,EACA,MAAA,EACA,QAAA,EACA,QAAA,QAAA,KAeF,eClDA,MAAA,QtBEA,iBAAA,QsBAA,aAAA,QAEA,2BACE,MAAA,QD6CF,iBClDA,MAAA,QtBEA,iBAAA,QsBAA,aAAA,QAEA,6BACE,MAAA,QD6CF,eClDA,MAAA,QtBEA,iBAAA,QsBAA,aAAA,QAEA,2BACE,MAAA,QD6CF,YClDA,MAAA,QtBEA,iBAAA,QsBAA,aAAA,QAEA,wBACE,MAAA,QD6CF,eClDA,MAAA,QtBEA,iBAAA,QsBAA,aAAA,QAEA,2BACE,MAAA,QD6CF,cClDA,MAAA,QtBEA,iBAAA,QsBAA,aAAA,QAEA,0BACE,MAAA,QD6CF,aClDA,MAAA,QtBEA,iBAAA,QsBAA,aAAA,QAEA,yBACE,MAAA,QD6CF,YClDA,MAAA,QtBEA,iBAAA,QsBAA,aAAA,QAEA,wBACE,MAAA,QCHF,wCACE,GAAK,sBAAA,MADP,gCACE,GAAK,sBAAA,MAKT,UACE,QAAA,KACA,OAAA,KACA,SAAA,OxCwRI,UAAA,OwCtRJ,iBAAA,QvCIE,cAAA,OuCCJ,cACE,QAAA,KACA,eAAA,OACA,gBAAA,OACA,SAAA,OACA,MAAA,KACA,WAAA,OACA,YAAA,OACA,iBAAA,QxBZI,WAAA,MAAA,IAAA,KAIA,uCwBAN,cxBCQ,WAAA,MwBWR,sBvBYE,iBAAA,iKuBVA,gBAAA,KAAA,KAIA,uBACE,kBAAA,GAAA,OAAA,SAAA,qBAAA,UAAA,GAAA,OAAA,SAAA,qBAGE,uCAJJ,uBAKM,kBAAA,KAAA,UAAA,MCvCR,YACE,QAAA,KACA,eAAA,OAGA,aAAA,EACA,cAAA,ExCSE,cAAA,OwCLJ,qBACE,gBAAA,KACA,cAAA,QAEA,gCAEE,QAAA,uBAAA,KACA,kBAAA,QAUJ,wBACE,MAAA,KACA,MAAA,QACA,WAAA,QAGA,8BAAA,8BAEE,QAAA,EACA,MAAA,QACA,gBAAA,KACA,iBAAA,QAGF,+BACE,MAAA,QACA,iBAAA,QASJ,iBACE,SAAA,SACA,QAAA,MACA,QAAA,MAAA,KACA,MAAA,QACA,gBAAA,KACA,iBAAA,KACA,OAAA,IAAA,MAAA,iBAEA,6BxCrCE,uBAAA,QACA,wBAAA,QwCwCF,4BxC3BE,2BAAA,QACA,0BAAA,QwC8BF,0BAAA,0BAEE,MAAA,QACA,eAAA,KACA,iBAAA,KAIF,wBACE,QAAA,EACA,MAAA,KACA,iBAAA,QACA,aAAA,QAGF,kCACE,iBAAA,EAEA,yCACE,WAAA,KACA,iBAAA,IAcF,uBACE,eAAA,IAGE,oDxCrCJ,0BAAA,OAZA,wBAAA,EwCsDI,mDxCtDJ,wBAAA,OAYA,0BAAA,EwC+CI,+CACE,WAAA,EAGF,yDACE,iBAAA,IACA,kBAAA,EAEA,gEACE,YAAA,KACA,kBAAA,IjCpER,yBiC4CA,0BACE,eAAA,IAGE,uDxCrCJ,0BAAA,OAZA,wBAAA,EwCsDI,sDxCtDJ,wBAAA,OAYA,0BAAA,EwC+CI,kDACE,WAAA,EAGF,4DACE,iBAAA,IACA,kBAAA,EAEA,mEACE,YAAA,KACA,kBAAA,KjCpER,yBiC4CA,0BACE,eAAA,IAGE,uDxCrCJ,0BAAA,OAZA,wBAAA,EwCsDI,sDxCtDJ,wBAAA,OAYA,0BAAA,EwC+CI,kDACE,WAAA,EAGF,4DACE,iBAAA,IACA,kBAAA,EAEA,mEACE,YAAA,KACA,kBAAA,KjCpER,yBiC4CA,0BACE,eAAA,IAGE,uDxCrCJ,0BAAA,OAZA,wBAAA,EwCsDI,sDxCtDJ,wBAAA,OAYA,0BAAA,EwC+CI,kDACE,WAAA,EAGF,4DACE,iBAAA,IACA,kBAAA,EAEA,mEACE,YAAA,KACA,kBAAA,KjCpER,0BiC4CA,0BACE,eAAA,IAGE,uDxCrCJ,0BAAA,OAZA,wBAAA,EwCsDI,sDxCtDJ,wBAAA,OAYA,0BAAA,EwC+CI,kDACE,WAAA,EAGF,4DACE,iBAAA,IACA,kBAAA,EAEA,mEACE,YAAA,KACA,kBAAA,KjCpER,0BiC4CA,2BACE,eAAA,IAGE,wDxCrCJ,0BAAA,OAZA,wBAAA,EwCsDI,uDxCtDJ,wBAAA,OAYA,0BAAA,EwC+CI,mDACE,WAAA,EAGF,6DACE,iBAAA,IACA,kBAAA,EAEA,oEACE,YAAA,KACA,kBAAA,KAcZ,kBxC9HI,cAAA,EwCiIF,mCACE,aAAA,EAAA,EAAA,IAEA,8CACE,oBAAA,ECpJJ,yBACE,MAAA,QACA,iBAAA,QAGE,sDAAA,sDAEE,MAAA,QACA,iBAAA,QAGF,uDACE,MAAA,KACA,iBAAA,QACA,aAAA,QAdN,2BACE,MAAA,QACA,iBAAA,QAGE,wDAAA,wDAEE,MAAA,QACA,iBAAA,QAGF,yDACE,MAAA,KACA,iBAAA,QACA,aAAA,QAdN,yBACE,MAAA,QACA,iBAAA,QAGE,sDAAA,sDAEE,MAAA,QACA,iBAAA,QAGF,uDACE,MAAA,KACA,iBAAA,QACA,aAAA,QAdN,sBACE,MAAA,QACA,iBAAA,QAGE,mDAAA,mDAEE,MAAA,QACA,iBAAA,QAGF,oDACE,MAAA,KACA,iBAAA,QACA,aAAA,QAdN,yBACE,MAAA,QACA,iBAAA,QAGE,sDAAA,sDAEE,MAAA,QACA,iBAAA,QAGF,uDACE,MAAA,KACA,iBAAA,QACA,aAAA,QAdN,wBACE,MAAA,QACA,iBAAA,QAGE,qDAAA,qDAEE,MAAA,QACA,iBAAA,QAGF,sDACE,MAAA,KACA,iBAAA,QACA,aAAA,QAdN,uBACE,MAAA,QACA,iBAAA,QAGE,oDAAA,oDAEE,MAAA,QACA,iBAAA,QAGF,qDACE,MAAA,KACA,iBAAA,QACA,aAAA,QAdN,sBACE,MAAA,QACA,iBAAA,QAGE,mDAAA,mDAEE,MAAA,QACA,iBAAA,QAGF,oDACE,MAAA,KACA,iBAAA,QACA,aAAA,QCbR,WACE,WAAA,YACA,MAAA,IACA,OAAA,IACA,QAAA,MAAA,MACA,MAAA,KACA,WAAA,YAAA,0TAAA,MAAA,CAAA,IAAA,KAAA,UACA,OAAA,E1COE,cAAA,O0CLF,QAAA,GAGA,iBACE,MAAA,KACA,gBAAA,KACA,QAAA,IAGF,iBACE,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,OAAA,qBACA,QAAA,EAGF,oBAAA,oBAEE,eAAA,KACA,oBAAA,KAAA,iBAAA,KAAA,YAAA,KACA,QAAA,IAIJ,iBACE,OAAA,UAAA,gBAAA,iBCtCF,OACE,MAAA,MACA,UAAA,K5CmSI,UAAA,Q4ChSJ,eAAA,KACA,iBAAA,sBACA,gBAAA,YACA,OAAA,IAAA,MAAA,eACA,WAAA,EAAA,MAAA,KAAA,gB3CUE,cAAA,O2CPF,eACE,QAAA,EAGF,kBACE,QAAA,KAIJ,iBACE,MAAA,oBAAA,MAAA,iBAAA,MAAA,YACA,UAAA,KACA,eAAA,KAEA,mCACE,cAAA,OAIJ,cACE,QAAA,KACA,YAAA,OACA,QAAA,MAAA,OACA,MAAA,QACA,iBAAA,sBACA,gBAAA,YACA,cAAA,IAAA,MAAA,gB3CVE,uBAAA,mBACA,wBAAA,mB2CYF,yBACE,aAAA,SACA,YAAA,OAIJ,YACE,QAAA,OACA,UAAA,WC1CF,OACE,SAAA,MACA,IAAA,EACA,KAAA,EACA,QAAA,KACA,QAAA,KACA,MAAA,KACA,OAAA,KACA,WAAA,OACA,WAAA,KAGA,QAAA,EAOF,cACE,SAAA,SACA,MAAA,KACA,OAAA,MAEA,eAAA,KAGA,0B7BlBI,WAAA,UAAA,IAAA,S6BoBF,UAAA,mB7BhBE,uC6BcJ,0B7BbM,WAAA,M6BiBN,0BACE,UAAA,KAIF,kCACE,UAAA,YAIJ,yBACE,OAAA,kBAEA,wCACE,WAAA,KACA,SAAA,OAGF,qCACE,WAAA,KAIJ,uBACE,QAAA,KACA,YAAA,OACA,WAAA,kBAIF,eACE,SAAA,SACA,QAAA,KACA,eAAA,OACA,MAAA,KAGA,eAAA,KACA,iBAAA,KACA,gBAAA,YACA,OAAA,IAAA,MAAA,e5C3DE,cAAA,M4C+DF,QAAA,EAIF,gBCpFE,SAAA,MACA,IAAA,EACA,KAAA,EACA,QAAA,KACA,MAAA,MACA,OAAA,MACA,iBAAA,KAGA,qBAAS,QAAA,EACT,qBAAS,QAAA,GDgFX,cACE,QAAA,KACA,YAAA,EACA,YAAA,OACA,gBAAA,cACA,QAAA,KAAA,KACA,cAAA,IAAA,MAAA,Q5CtEE,uBAAA,kBACA,wBAAA,kB4CwEF,yBACE,QAAA,MAAA,MACA,OAAA,OAAA,OAAA,OAAA,KAKJ,aACE,cAAA,EACA,YAAA,IAKF,YACE,SAAA,SAGA,KAAA,EAAA,EAAA,KACA,QAAA,KAIF,cACE,QAAA,KACA,UAAA,KACA,YAAA,EACA,YAAA,OACA,gBAAA,SACA,QAAA,OACA,WAAA,IAAA,MAAA,Q5CzFE,2BAAA,kBACA,0BAAA,kB4C8FF,gBACE,OAAA,OrC3EA,yBqCkFF,cACE,UAAA,MACA,OAAA,QAAA,KAGF,yBACE,OAAA,oBAGF,uBACE,WAAA,oBAOF,UAAY,UAAA,OrCnGV,yBqCuGF,U9CywKF,U8CvwKI,UAAA,OrCzGA,0BqC8GF,UAAY,UAAA,QASV,kBACE,MAAA,MACA,UAAA,KACA,OAAA,KACA,OAAA,EAEA,iCACE,OAAA,KACA,OAAA,E5C3KJ,cAAA,E4C+KE,gC5C/KF,cAAA,E4CmLE,8BACE,WAAA,KAGF,gC5CvLF,cAAA,EOyDA,4BqC0GA,0BACE,MAAA,MACA,UAAA,KACA,OAAA,KACA,OAAA,EAEA,yCACE,OAAA,KACA,OAAA,E5C3KJ,cAAA,E4C+KE,wC5C/KF,cAAA,E4CmLE,sCACE,WAAA,KAGF,wC5CvLF,cAAA,GOyDA,4BqC0GA,0BACE,MAAA,MACA,UAAA,KACA,OAAA,KACA,OAAA,EAEA,yCACE,OAAA,KACA,OAAA,E5C3KJ,cAAA,E4C+KE,wC5C/KF,cAAA,E4CmLE,sCACE,WAAA,KAGF,wC5CvLF,cAAA,GOyDA,4BqC0GA,0BACE,MAAA,MACA,UAAA,KACA,OAAA,KACA,OAAA,EAEA,yCACE,OAAA,KACA,OAAA,E5C3KJ,cAAA,E4C+KE,wC5C/KF,cAAA,E4CmLE,sCACE,WAAA,KAGF,wC5CvLF,cAAA,GOyDA,6BqC0GA,0BACE,MAAA,MACA,UAAA,KACA,OAAA,KACA,OAAA,EAEA,yCACE,OAAA,KACA,OAAA,E5C3KJ,cAAA,E4C+KE,wC5C/KF,cAAA,E4CmLE,sCACE,WAAA,KAGF,wC5CvLF,cAAA,GOyDA,6BqC0GA,2BACE,MAAA,MACA,UAAA,KACA,OAAA,KACA,OAAA,EAEA,0CACE,OAAA,KACA,OAAA,E5C3KJ,cAAA,E4C+KE,yC5C/KF,cAAA,E4CmLE,uCACE,WAAA,KAGF,yC5CvLF,cAAA,G8ClBJ,SACE,SAAA,SACA,QAAA,KACA,QAAA,MACA,OAAA,ECJA,YAAA,0BAEA,WAAA,OACA,YAAA,IACA,YAAA,IACA,WAAA,KACA,WAAA,MACA,gBAAA,KACA,YAAA,KACA,eAAA,KACA,eAAA,OACA,WAAA,OACA,aAAA,OACA,YAAA,OACA,WAAA,KhDsRI,UAAA,Q+C1RJ,UAAA,WACA,QAAA,EAEA,cAAS,QAAA,GAET,wBACE,SAAA,SACA,QAAA,MACA,MAAA,MACA,OAAA,MAEA,gCACE,SAAA,SACA,QAAA,GACA,aAAA,YACA,aAAA,MAKN,6CAAA,gBACE,QAAA,MAAA,EAEA,4DAAA,+BACE,OAAA,EAEA,oEAAA,uCACE,IAAA,KACA,aAAA,MAAA,MAAA,EACA,iBAAA,KAKN,+CAAA,gBACE,QAAA,EAAA,MAEA,8DAAA,+BACE,KAAA,EACA,MAAA,MACA,OAAA,MAEA,sEAAA,uCACE,MAAA,KACA,aAAA,MAAA,MAAA,MAAA,EACA,mBAAA,KAKN,gDAAA,mBACE,QAAA,MAAA,EAEA,+DAAA,kCACE,IAAA,EAEA,uEAAA,0CACE,OAAA,KACA,aAAA,EAAA,MAAA,MACA,oBAAA,KAKN,8CAAA,kBACE,QAAA,EAAA,MAEA,6DAAA,iCACE,MAAA,EACA,MAAA,MACA,OAAA,MAEA,qEAAA,yCACE,KAAA,KACA,aAAA,MAAA,EAAA,MAAA,MACA,kBAAA,KAqBN,eACE,UAAA,MACA,QAAA,OAAA,MACA,MAAA,KACA,WAAA,OACA,iBAAA,K9C7FE,cAAA,OgDnBJ,SACE,SAAA,SACA,IAAA,EACA,KAAA,EACA,QAAA,KACA,QAAA,MACA,UAAA,MDLA,YAAA,0BAEA,WAAA,OACA,YAAA,IACA,YAAA,IACA,WAAA,KACA,WAAA,MACA,gBAAA,KACA,YAAA,KACA,eAAA,KACA,eAAA,OACA,WAAA,OACA,aAAA,OACA,YAAA,OACA,WAAA,KhDsRI,UAAA,QiDzRJ,UAAA,WACA,iBAAA,KACA,gBAAA,YACA,OAAA,IAAA,MAAA,ehDIE,cAAA,MgDAF,wBACE,SAAA,SACA,QAAA,MACA,MAAA,KACA,OAAA,MAEA,+BAAA,gCAEE,SAAA,SACA,QAAA,MACA,QAAA,GACA,aAAA,YACA,aAAA,MAMJ,4DAAA,+BACE,OAAA,mBAEA,oEAAA,uCACE,OAAA,EACA,aAAA,MAAA,MAAA,EACA,iBAAA,gBAGF,mEAAA,sCACE,OAAA,IACA,aAAA,MAAA,MAAA,EACA,iBAAA,KAMJ,8DAAA,+BACE,KAAA,mBACA,MAAA,MACA,OAAA,KAEA,sEAAA,uCACE,KAAA,EACA,aAAA,MAAA,MAAA,MAAA,EACA,mBAAA,gBAGF,qEAAA,sCACE,KAAA,IACA,aAAA,MAAA,MAAA,MAAA,EACA,mBAAA,KAMJ,+DAAA,kCACE,IAAA,mBAEA,uEAAA,0CACE,IAAA,EACA,aAAA,EAAA,MAAA,MAAA,MACA,oBAAA,gBAGF,sEAAA,yCACE,IAAA,IACA,aAAA,EAAA,MAAA,MAAA,MACA,oBAAA,KAKJ,wEAAA,2CACE,SAAA,SACA,IAAA,EACA,KAAA,IACA,QAAA,MACA,MAAA,KACA,YAAA,OACA,QAAA,GACA,cAAA,IAAA,MAAA,QAKF,6DAAA,iCACE,MAAA,mBACA,MAAA,MACA,OAAA,KAEA,qEAAA,yCACE,MAAA,EACA,aAAA,MAAA,EAAA,MAAA,MACA,kBAAA,gBAGF,oEAAA,wCACE,MAAA,IACA,aAAA,MAAA,EAAA,MAAA,MACA,kBAAA,KAqBN,gBACE,QAAA,MAAA,KACA,cAAA,EjDuJI,UAAA,KiDpJJ,iBAAA,QACA,cAAA,IAAA,MAAA,ehDtHE,uBAAA,kBACA,wBAAA,kBgDwHF,sBACE,QAAA,KAIJ,cACE,QAAA,KAAA,KACA,MAAA,QC/IF,UACE,SAAA,SAGF,wBACE,aAAA,MAGF,gBACE,SAAA,SACA,MAAA,KACA,SAAA,OCtBA,uBACE,QAAA,MACA,MAAA,KACA,QAAA,GDuBJ,eACE,SAAA,SACA,QAAA,KACA,MAAA,KACA,MAAA,KACA,aAAA,MACA,4BAAA,OAAA,oBAAA,OlClBI,WAAA,UAAA,IAAA,YAIA,uCkCQN,elCPQ,WAAA,MjBgzLR,oBACA,oBmDhyLA,sBAGE,QAAA,MnDmyLF,0BmD/xLA,8CAEE,UAAA,iBnDkyLF,4BmD/xLA,4CAEE,UAAA,kBAWA,8BACE,QAAA,EACA,oBAAA,QACA,UAAA,KnD0xLJ,uDACA,qDmDxxLE,qCAGE,QAAA,EACA,QAAA,EnDyxLJ,yCmDtxLE,2CAEE,QAAA,EACA,QAAA,ElC/DE,WAAA,QAAA,GAAA,IAIA,uCjBq1LN,yCmD7xLE,2ClCvDM,WAAA,MjB01LR,uBmDtxLA,uBAEE,SAAA,SACA,IAAA,EACA,OAAA,EACA,QAAA,EAEA,QAAA,KACA,YAAA,OACA,gBAAA,OACA,MAAA,IACA,QAAA,EACA,MAAA,KACA,WAAA,OACA,WAAA,IACA,OAAA,EACA,QAAA,GlCzFI,WAAA,QAAA,KAAA,KAIA,uCjB82LN,uBmDzyLA,uBlCpEQ,WAAA,MjBm3LR,6BADA,6BmD1xLE,6BAAA,6BAEE,MAAA,KACA,gBAAA,KACA,QAAA,EACA,QAAA,GAGJ,uBACE,KAAA,EAGF,uBACE,MAAA,EnD8xLF,4BmDzxLA,4BAEE,QAAA,aACA,MAAA,KACA,OAAA,KACA,kBAAA,UACA,oBAAA,IACA,gBAAA,KAAA,KAWF,4BACE,iBAAA,wPAEF,4BACE,iBAAA,yPAQF,qBACE,SAAA,SACA,MAAA,EACA,OAAA,EACA,KAAA,EACA,QAAA,EACA,QAAA,KACA,gBAAA,OACA,QAAA,EAEA,aAAA,IACA,cAAA,KACA,YAAA,IACA,WAAA,KAEA,sCACE,WAAA,YACA,KAAA,EAAA,EAAA,KACA,MAAA,KACA,OAAA,IACA,QAAA,EACA,aAAA,IACA,YAAA,IACA,YAAA,OACA,OAAA,QACA,iBAAA,KACA,gBAAA,YACA,OAAA,EAEA,WAAA,KAAA,MAAA,YACA,cAAA,KAAA,MAAA,YACA,QAAA,GlC5KE,WAAA,QAAA,IAAA,KAIA,uCkCwJJ,sClCvJM,WAAA,MkC2KN,6BACE,QAAA,EASJ,kBACE,SAAA,SACA,MAAA,IACA,OAAA,QACA,KAAA,IACA,YAAA,QACA,eAAA,QACA,MAAA,KACA,WAAA,OnDoxLF,2CmD9wLE,2CAEE,OAAA,UAAA,eAGF,qDACE,iBAAA,KAGF,iCACE,MAAA,KE7NJ,kCACE,GAAK,UAAA,gBADP,0BACE,GAAK,UAAA,gBAIP,gBACE,QAAA,aACA,MAAA,KACA,OAAA,KACA,eAAA,QACA,OAAA,MAAA,MAAA,aACA,mBAAA,YAEA,cAAA,IACA,kBAAA,KAAA,OAAA,SAAA,eAAA,UAAA,KAAA,OAAA,SAAA,eAGF,mBACE,MAAA,KACA,OAAA,KACA,aAAA,KAQF,gCACE,GACE,UAAA,SAEF,IACE,QAAA,EACA,UAAA,MANJ,wBACE,GACE,UAAA,SAEF,IACE,QAAA,EACA,UAAA,MAKJ,cACE,QAAA,aACA,MAAA,KACA,OAAA,KACA,eAAA,QACA,iBAAA,aAEA,cAAA,IACA,QAAA,EACA,kBAAA,KAAA,OAAA,SAAA,aAAA,UAAA,KAAA,OAAA,SAAA,aAGF,iBACE,MAAA,KACA,OAAA,KAIA,uCACE,gBrDo/LJ,cqDl/LM,2BAAA,KAAA,mBAAA,MCjEN,WACE,SAAA,MACA,OAAA,EACA,QAAA,KACA,QAAA,KACA,eAAA,OACA,UAAA,KAEA,WAAA,OACA,iBAAA,KACA,gBAAA,YACA,QAAA,ErCKI,WAAA,UAAA,IAAA,YAIA,uCqCpBN,WrCqBQ,WAAA,MqCLR,oBPdE,SAAA,MACA,IAAA,EACA,KAAA,EACA,QAAA,KACA,MAAA,MACA,OAAA,MACA,iBAAA,KAGA,yBAAS,QAAA,EACT,yBAAS,QAAA,GOQX,kBACE,QAAA,KACA,YAAA,OACA,gBAAA,cACA,QAAA,KAAA,KAEA,6BACE,QAAA,MAAA,MACA,WAAA,OACA,aAAA,OACA,cAAA,OAIJ,iBACE,cAAA,EACA,YAAA,IAGF,gBACE,UAAA,EACA,QAAA,KAAA,KACA,WAAA,KAGF,iBACE,IAAA,EACA,KAAA,EACA,MAAA,MACA,aAAA,IAAA,MAAA,eACA,UAAA,kBAGF,eACE,IAAA,EACA,MAAA,EACA,MAAA,MACA,YAAA,IAAA,MAAA,eACA,UAAA,iBAGF,eACE,IAAA,EACA,MAAA,EACA,KAAA,EACA,OAAA,KACA,WAAA,KACA,cAAA,IAAA,MAAA,eACA,UAAA,kBAGF,kBACE,MAAA,EACA,KAAA,EACA,OAAA,KACA,WAAA,KACA,WAAA,IAAA,MAAA,eACA,UAAA,iBAGF,gBACE,UAAA,KCjFF,aACE,QAAA,aACA,WAAA,IACA,eAAA,OACA,OAAA,KACA,iBAAA,aACA,QAAA,GAEA,yBACE,QAAA,aACA,QAAA,GAKJ,gBACE,WAAA,KAGF,gBACE,WAAA,KAGF,gBACE,WAAA,MAKA,+BACE,kBAAA,iBAAA,GAAA,YAAA,SAAA,UAAA,iBAAA,GAAA,YAAA,SAIJ,oCACE,IACE,QAAA,IAFJ,4BACE,IACE,QAAA,IAIJ,kBACE,mBAAA,8DAAA,WAAA,8DACA,kBAAA,KAAA,KAAA,UAAA,KAAA,KACA,kBAAA,iBAAA,GAAA,OAAA,SAAA,UAAA,iBAAA,GAAA,OAAA,SAGF,oCACE,KACE,sBAAA,MAAA,GAAA,cAAA,MAAA,IAFJ,4BACE,KACE,sBAAA,MAAA,GAAA,cAAA,MAAA,IH9CF,iBACE,QAAA,MACA,MAAA,KACA,QAAA,GIJF,cACE,MAAA,QAGE,oBAAA,oBAEE,MAAA,QANN,gBACE,MAAA,QAGE,sBAAA,sBAEE,MAAA,QANN,cACE,MAAA,QAGE,oBAAA,oBAEE,MAAA,QANN,WACE,MAAA,QAGE,iBAAA,iBAEE,MAAA,QANN,cACE,MAAA,QAGE,oBAAA,oBAEE,MAAA,QANN,aACE,MAAA,QAGE,mBAAA,mBAEE,MAAA,QANN,YACE,MAAA,QAGE,kBAAA,kBAEE,MAAA,QANN,WACE,MAAA,QAGE,iBAAA,iBAEE,MAAA,QCLR,OACE,SAAA,SACA,MAAA,KAEA,eACE,QAAA,MACA,YAAA,uBACA,QAAA,GAGF,SACE,SAAA,SACA,IAAA,EACA,KAAA,EACA,MAAA,KACA,OAAA,KAKF,WACE,kBAAA,KADF,WACE,kBAAA,mBADF,YACE,kBAAA,oBADF,YACE,kBAAA,oBCrBJ,WACE,SAAA,MACA,IAAA,EACA,MAAA,EACA,KAAA,EACA,QAAA,KAGF,cACE,SAAA,MACA,MAAA,EACA,OAAA,EACA,KAAA,EACA,QAAA,KAQE,YACE,SAAA,eAAA,SAAA,OACA,IAAA,EACA,QAAA,KjDqCF,yBiDxCA,eACE,SAAA,eAAA,SAAA,OACA,IAAA,EACA,QAAA,MjDqCF,yBiDxCA,eACE,SAAA,eAAA,SAAA,OACA,IAAA,EACA,QAAA,MjDqCF,yBiDxCA,eACE,SAAA,eAAA,SAAA,OACA,IAAA,EACA,QAAA,MjDqCF,0BiDxCA,eACE,SAAA,eAAA,SAAA,OACA,IAAA,EACA,QAAA,MjDqCF,0BiDxCA,gBACE,SAAA,eAAA,SAAA,OACA,IAAA,EACA,QAAA,MCzBN,QACE,QAAA,KACA,eAAA,IACA,YAAA,OACA,WAAA,QAGF,QACE,QAAA,KACA,KAAA,EAAA,EAAA,KACA,eAAA,OACA,WAAA,QCRF,iB5Dk4MA,0D6D93ME,SAAA,mBACA,MAAA,cACA,OAAA,cACA,QAAA,YACA,OAAA,eACA,SAAA,iBACA,KAAA,wBACA,YAAA,iBACA,OAAA,YCXA,uBACE,SAAA,SACA,IAAA,EACA,MAAA,EACA,OAAA,EACA,KAAA,EACA,QAAA,EACA,QAAA,GCRJ,eCAE,SAAA,OACA,cAAA,SACA,YAAA,OCNF,IACE,QAAA,aACA,WAAA,QACA,MAAA,IACA,WAAA,IACA,iBAAA,aACA,QAAA,ICyDM,gBAOI,eAAA,mBAPJ,WAOI,eAAA,cAPJ,cAOI,eAAA,iBAPJ,cAOI,eAAA,iBAPJ,mBAOI,eAAA,sBAPJ,gBAOI,eAAA,mBAPJ,aAOI,MAAA,eAPJ,WAOI,MAAA,gBAPJ,YAOI,MAAA,eAPJ,WAOI,QAAA,YAPJ,YAOI,QAAA,cAPJ,YAOI,QAAA,aAPJ,YAOI,QAAA,cAPJ,aAOI,QAAA,YAPJ,eAOI,SAAA,eAPJ,iBAOI,SAAA,iBAPJ,kBAOI,SAAA,kBAPJ,iBAOI,SAAA,iBAPJ,UAOI,QAAA,iBAPJ,gBAOI,QAAA,uBAPJ,SAOI,QAAA,gBAPJ,QAOI,QAAA,eAPJ,SAOI,QAAA,gBAPJ,aAOI,QAAA,oBAPJ,cAOI,QAAA,qBAPJ,QAOI,QAAA,eAPJ,eAOI,QAAA,sBAPJ,QAOI,QAAA,eAPJ,QAOI,WAAA,EAAA,MAAA,KAAA,0BAPJ,WAOI,WAAA,EAAA,QAAA,OAAA,2BAPJ,WAOI,WAAA,EAAA,KAAA,KAAA,2BAPJ,aAOI,WAAA,eAPJ,iBAOI,SAAA,iBAPJ,mBAOI,SAAA,mBAPJ,mBAOI,SAAA,mBAPJ,gBAOI,SAAA,gBAPJ,iBAOI,SAAA,yBAAA,SAAA,iBAPJ,OAOI,IAAA,YAPJ,QAOI,IAAA,cAPJ,SAOI,IAAA,eAPJ,UAOI,OAAA,YAPJ,WAOI,OAAA,cAPJ,YAOI,OAAA,eAPJ,SAOI,KAAA,YAPJ,UAOI,KAAA,cAPJ,WAOI,KAAA,eAPJ,OAOI,MAAA,YAPJ,QAOI,MAAA,cAPJ,SAOI,MAAA,eAPJ,kBAOI,UAAA,+BAPJ,oBAOI,UAAA,2BAPJ,oBAOI,UAAA,2BAPJ,QAOI,OAAA,IAAA,MAAA,kBAPJ,UAOI,OAAA,YAPJ,YAOI,WAAA,IAAA,MAAA,kBAPJ,cAOI,WAAA,YAPJ,YAOI,aAAA,IAAA,MAAA,kBAPJ,cAOI,aAAA,YAPJ,eAOI,cAAA,IAAA,MAAA,kBAPJ,iBAOI,cAAA,YAPJ,cAOI,YAAA,IAAA,MAAA,kBAPJ,gBAOI,YAAA,YAPJ,gBAOI,aAAA,kBAPJ,kBAOI,aAAA,kBAPJ,gBAOI,aAAA,kBAPJ,aAOI,aAAA,kBAPJ,gBAOI,aAAA,kBAPJ,eAOI,aAAA,kBAPJ,cAOI,aAAA,kBAPJ,aAOI,aAAA,kBAPJ,cAOI,aAAA,eAPJ,UAOI,aAAA,cAPJ,UAOI,aAAA,cAPJ,UAOI,aAAA,cAPJ,UAOI,aAAA,cAPJ,UAOI,aAAA,cAPJ,MAOI,MAAA,cAPJ,MAOI,MAAA,cAPJ,MAOI,MAAA,cAPJ,OAOI,MAAA,eAPJ,QAOI,MAAA,eAPJ,QAOI,UAAA,eAPJ,QAOI,MAAA,gBAPJ,YAOI,UAAA,gBAPJ,MAOI,OAAA,cAPJ,MAOI,OAAA,cAPJ,MAOI,OAAA,cAPJ,OAOI,OAAA,eAPJ,QAOI,OAAA,eAPJ,QAOI,WAAA,eAPJ,QAOI,OAAA,gBAPJ,YAOI,WAAA,gBAPJ,WAOI,KAAA,EAAA,EAAA,eAPJ,UAOI,eAAA,cAPJ,aAOI,eAAA,iBAPJ,kBAOI,eAAA,sBAPJ,qBAOI,eAAA,yBAPJ,aAOI,UAAA,YAPJ,aAOI,UAAA,YAPJ,eAOI,YAAA,YAPJ,eAOI,YAAA,YAPJ,WAOI,UAAA,eAPJ,aAOI,UAAA,iBAPJ,mBAOI,UAAA,uBAPJ,OAOI,IAAA,YAPJ,OAOI,IAAA,iBAPJ,OAOI,IAAA,gBAPJ,OAOI,IAAA,eAPJ,OAOI,IAAA,iBAPJ,OAOI,IAAA,eAPJ,uBAOI,gBAAA,qBAPJ,qBAOI,gBAAA,mBAPJ,wBAOI,gBAAA,iBAPJ,yBAOI,gBAAA,wBAPJ,wBAOI,gBAAA,uBAPJ,wBAOI,gBAAA,uBAPJ,mBAOI,YAAA,qBAPJ,iBAOI,YAAA,mBAPJ,oBAOI,YAAA,iBAPJ,sBAOI,YAAA,mBAPJ,qBAOI,YAAA,kBAPJ,qBAOI,cAAA,qBAPJ,mBAOI,cAAA,mBAPJ,sBAOI,cAAA,iBAPJ,uBAOI,cAAA,wBAPJ,sBAOI,cAAA,uBAPJ,uBAOI,cAAA,kBAPJ,iBAOI,WAAA,eAPJ,kBAOI,WAAA,qBAPJ,gBAOI,WAAA,mBAPJ,mBAOI,WAAA,iBAPJ,qBAOI,WAAA,mBAPJ,oBAOI,WAAA,kBAPJ,aAOI,MAAA,aAPJ,SAOI,MAAA,YAPJ,SAOI,MAAA,YAPJ,SAOI,MAAA,YAPJ,SAOI,MAAA,YAPJ,SAOI,MAAA,YAPJ,SAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,KAOI,OAAA,YAPJ,KAOI,OAAA,iBAPJ,KAOI,OAAA,gBAPJ,KAOI,OAAA,eAPJ,KAOI,OAAA,iBAPJ,KAOI,OAAA,eAPJ,QAOI,OAAA,eAPJ,MAOI,aAAA,YAAA,YAAA,YAPJ,MAOI,aAAA,iBAAA,YAAA,iBAPJ,MAOI,aAAA,gBAAA,YAAA,gBAPJ,MAOI,aAAA,eAAA,YAAA,eAPJ,MAOI,aAAA,iBAAA,YAAA,iBAPJ,MAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,MAOI,WAAA,YAAA,cAAA,YAPJ,MAOI,WAAA,iBAAA,cAAA,iBAPJ,MAOI,WAAA,gBAAA,cAAA,gBAPJ,MAOI,WAAA,eAAA,cAAA,eAPJ,MAOI,WAAA,iBAAA,cAAA,iBAPJ,MAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,MAOI,WAAA,YAPJ,MAOI,WAAA,iBAPJ,MAOI,WAAA,gBAPJ,MAOI,WAAA,eAPJ,MAOI,WAAA,iBAPJ,MAOI,WAAA,eAPJ,SAOI,WAAA,eAPJ,MAOI,aAAA,YAPJ,MAOI,aAAA,iBAPJ,MAOI,aAAA,gBAPJ,MAOI,aAAA,eAPJ,MAOI,aAAA,iBAPJ,MAOI,aAAA,eAPJ,SAOI,aAAA,eAPJ,MAOI,cAAA,YAPJ,MAOI,cAAA,iBAPJ,MAOI,cAAA,gBAPJ,MAOI,cAAA,eAPJ,MAOI,cAAA,iBAPJ,MAOI,cAAA,eAPJ,SAOI,cAAA,eAPJ,MAOI,YAAA,YAPJ,MAOI,YAAA,iBAPJ,MAOI,YAAA,gBAPJ,MAOI,YAAA,eAPJ,MAOI,YAAA,iBAPJ,MAOI,YAAA,eAPJ,SAOI,YAAA,eAPJ,KAOI,QAAA,YAPJ,KAOI,QAAA,iBAPJ,KAOI,QAAA,gBAPJ,KAOI,QAAA,eAPJ,KAOI,QAAA,iBAPJ,KAOI,QAAA,eAPJ,MAOI,cAAA,YAAA,aAAA,YAPJ,MAOI,cAAA,iBAAA,aAAA,iBAPJ,MAOI,cAAA,gBAAA,aAAA,gBAPJ,MAOI,cAAA,eAAA,aAAA,eAPJ,MAOI,cAAA,iBAAA,aAAA,iBAPJ,MAOI,cAAA,eAAA,aAAA,eAPJ,MAOI,YAAA,YAAA,eAAA,YAPJ,MAOI,YAAA,iBAAA,eAAA,iBAPJ,MAOI,YAAA,gBAAA,eAAA,gBAPJ,MAOI,YAAA,eAAA,eAAA,eAPJ,MAOI,YAAA,iBAAA,eAAA,iBAPJ,MAOI,YAAA,eAAA,eAAA,eAPJ,MAOI,YAAA,YAPJ,MAOI,YAAA,iBAPJ,MAOI,YAAA,gBAPJ,MAOI,YAAA,eAPJ,MAOI,YAAA,iBAPJ,MAOI,YAAA,eAPJ,MAOI,cAAA,YAPJ,MAOI,cAAA,iBAPJ,MAOI,cAAA,gBAPJ,MAOI,cAAA,eAPJ,MAOI,cAAA,iBAPJ,MAOI,cAAA,eAPJ,MAOI,eAAA,YAPJ,MAOI,eAAA,iBAPJ,MAOI,eAAA,gBAPJ,MAOI,eAAA,eAPJ,MAOI,eAAA,iBAPJ,MAOI,eAAA,eAPJ,MAOI,aAAA,YAPJ,MAOI,aAAA,iBAPJ,MAOI,aAAA,gBAPJ,MAOI,aAAA,eAPJ,MAOI,aAAA,iBAPJ,MAOI,aAAA,eAPJ,gBAOI,YAAA,mCAPJ,MAOI,UAAA,iCAPJ,MAOI,UAAA,gCAPJ,MAOI,UAAA,8BAPJ,MAOI,UAAA,gCAPJ,MAOI,UAAA,kBAPJ,MAOI,UAAA,eAPJ,YAOI,WAAA,iBAPJ,YAOI,WAAA,iBAPJ,UAOI,YAAA,cAPJ,YAOI,YAAA,kBAPJ,WAOI,YAAA,cAPJ,SAOI,YAAA,cAPJ,WAOI,YAAA,iBAPJ,MAOI,YAAA,YAPJ,OAOI,YAAA,eAPJ,SAOI,YAAA,cAPJ,OAOI,YAAA,YAPJ,YAOI,WAAA,eAPJ,UAOI,WAAA,gBAPJ,aAOI,WAAA,iBAPJ,sBAOI,gBAAA,eAPJ,2BAOI,gBAAA,oBAPJ,8BAOI,gBAAA,uBAPJ,gBAOI,eAAA,oBAPJ,gBAOI,eAAA,oBAPJ,iBAOI,eAAA,qBAPJ,WAOI,YAAA,iBAPJ,aAOI,YAAA,iBAPJ,YAOI,UAAA,qBAAA,WAAA,qBAPJ,cAIQ,kBAAA,EAGJ,MAAA,6DAPJ,gBAIQ,kBAAA,EAGJ,MAAA,+DAPJ,cAIQ,kBAAA,EAGJ,MAAA,6DAPJ,WAIQ,kBAAA,EAGJ,MAAA,0DAPJ,cAIQ,kBAAA,EAGJ,MAAA,6DAPJ,aAIQ,kBAAA,EAGJ,MAAA,4DAPJ,YAIQ,kBAAA,EAGJ,MAAA,2DAPJ,WAIQ,kBAAA,EAGJ,MAAA,0DAPJ,YAIQ,kBAAA,EAGJ,MAAA,2DAPJ,YAIQ,kBAAA,EAGJ,MAAA,2DAPJ,WAIQ,kBAAA,EAGJ,MAAA,0DAPJ,YAIQ,kBAAA,EAGJ,MAAA,kBAPJ,eAIQ,kBAAA,EAGJ,MAAA,yBAPJ,eAIQ,kBAAA,EAGJ,MAAA,+BAPJ,YAIQ,kBAAA,EAGJ,MAAA,kBAjBJ,iBACE,kBAAA,KADF,iBACE,kBAAA,IADF,iBACE,kBAAA,KADF,kBACE,kBAAA,EASF,YAIQ,gBAAA,EAGJ,iBAAA,2DAPJ,cAIQ,gBAAA,EAGJ,iBAAA,6DAPJ,YAIQ,gBAAA,EAGJ,iBAAA,2DAPJ,SAIQ,gBAAA,EAGJ,iBAAA,wDAPJ,YAIQ,gBAAA,EAGJ,iBAAA,2DAPJ,WAIQ,gBAAA,EAGJ,iBAAA,0DAPJ,UAIQ,gBAAA,EAGJ,iBAAA,yDAPJ,SAIQ,gBAAA,EAGJ,iBAAA,wDAPJ,UAIQ,gBAAA,EAGJ,iBAAA,yDAPJ,UAIQ,gBAAA,EAGJ,iBAAA,yDAPJ,SAIQ,gBAAA,EAGJ,iBAAA,wDAPJ,gBAIQ,gBAAA,EAGJ,iBAAA,sBAjBJ,eACE,gBAAA,IADF,eACE,gBAAA,KADF,eACE,gBAAA,IADF,eACE,gBAAA,KADF,gBACE,gBAAA,EASF,aAOI,iBAAA,6BAPJ,iBAOI,oBAAA,cAAA,iBAAA,cAAA,YAAA,cAPJ,kBAOI,oBAAA,eAAA,iBAAA,eAAA,YAAA,eAPJ,kBAOI,oBAAA,eAAA,iBAAA,eAAA,YAAA,eAPJ,SAOI,eAAA,eAPJ,SAOI,eAAA,eAPJ,SAOI,cAAA,iBAPJ,WAOI,cAAA,YAPJ,WAOI,cAAA,gBAPJ,WAOI,cAAA,iBAPJ,WAOI,cAAA,gBAPJ,gBAOI,cAAA,cAPJ,cAOI,cAAA,gBAPJ,aAOI,uBAAA,iBAAA,wBAAA,iBAPJ,aAOI,wBAAA,iBAAA,2BAAA,iBAPJ,gBAOI,2BAAA,iBAAA,0BAAA,iBAPJ,eAOI,0BAAA,iBAAA,uBAAA,iBAPJ,SAOI,WAAA,kBAPJ,WAOI,WAAA,iBzDPR,yByDAI,gBAOI,MAAA,eAPJ,cAOI,MAAA,gBAPJ,eAOI,MAAA,eAPJ,aAOI,QAAA,iBAPJ,mBAOI,QAAA,uBAPJ,YAOI,QAAA,gBAPJ,WAOI,QAAA,eAPJ,YAOI,QAAA,gBAPJ,gBAOI,QAAA,oBAPJ,iBAOI,QAAA,qBAPJ,WAOI,QAAA,eAPJ,kBAOI,QAAA,sBAPJ,WAOI,QAAA,eAPJ,cAOI,KAAA,EAAA,EAAA,eAPJ,aAOI,eAAA,cAPJ,gBAOI,eAAA,iBAPJ,qBAOI,eAAA,sBAPJ,wBAOI,eAAA,yBAPJ,gBAOI,UAAA,YAPJ,gBAOI,UAAA,YAPJ,kBAOI,YAAA,YAPJ,kBAOI,YAAA,YAPJ,cAOI,UAAA,eAPJ,gBAOI,UAAA,iBAPJ,sBAOI,UAAA,uBAPJ,UAOI,IAAA,YAPJ,UAOI,IAAA,iBAPJ,UAOI,IAAA,gBAPJ,UAOI,IAAA,eAPJ,UAOI,IAAA,iBAPJ,UAOI,IAAA,eAPJ,0BAOI,gBAAA,qBAPJ,wBAOI,gBAAA,mBAPJ,2BAOI,gBAAA,iBAPJ,4BAOI,gBAAA,wBAPJ,2BAOI,gBAAA,uBAPJ,2BAOI,gBAAA,uBAPJ,sBAOI,YAAA,qBAPJ,oBAOI,YAAA,mBAPJ,uBAOI,YAAA,iBAPJ,yBAOI,YAAA,mBAPJ,wBAOI,YAAA,kBAPJ,wBAOI,cAAA,qBAPJ,sBAOI,cAAA,mBAPJ,yBAOI,cAAA,iBAPJ,0BAOI,cAAA,wBAPJ,yBAOI,cAAA,uBAPJ,0BAOI,cAAA,kBAPJ,oBAOI,WAAA,eAPJ,qBAOI,WAAA,qBAPJ,mBAOI,WAAA,mBAPJ,sBAOI,WAAA,iBAPJ,wBAOI,WAAA,mBAPJ,uBAOI,WAAA,kBAPJ,gBAOI,MAAA,aAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,eAOI,MAAA,YAPJ,QAOI,OAAA,YAPJ,QAOI,OAAA,iBAPJ,QAOI,OAAA,gBAPJ,QAOI,OAAA,eAPJ,QAOI,OAAA,iBAPJ,QAOI,OAAA,eAPJ,WAOI,OAAA,eAPJ,SAOI,aAAA,YAAA,YAAA,YAPJ,SAOI,aAAA,iBAAA,YAAA,iBAPJ,SAOI,aAAA,gBAAA,YAAA,gBAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,aAAA,iBAAA,YAAA,iBAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,YAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,WAAA,YAAA,cAAA,YAPJ,SAOI,WAAA,iBAAA,cAAA,iBAPJ,SAOI,WAAA,gBAAA,cAAA,gBAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,iBAAA,cAAA,iBAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,YAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,YAPJ,SAOI,WAAA,iBAPJ,SAOI,WAAA,gBAPJ,SAOI,WAAA,eAPJ,SAOI,WAAA,iBAPJ,SAOI,WAAA,eAPJ,YAOI,WAAA,eAPJ,SAOI,aAAA,YAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,gBAPJ,SAOI,aAAA,eAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,eAPJ,YAOI,aAAA,eAPJ,SAOI,cAAA,YAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,gBAPJ,SAOI,cAAA,eAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,eAPJ,YAOI,cAAA,eAPJ,SAOI,YAAA,YAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,gBAPJ,SAOI,YAAA,eAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,eAPJ,YAOI,YAAA,eAPJ,QAOI,QAAA,YAPJ,QAOI,QAAA,iBAPJ,QAOI,QAAA,gBAPJ,QAOI,QAAA,eAPJ,QAOI,QAAA,iBAPJ,QAOI,QAAA,eAPJ,SAOI,cAAA,YAAA,aAAA,YAPJ,SAOI,cAAA,iBAAA,aAAA,iBAPJ,SAOI,cAAA,gBAAA,aAAA,gBAPJ,SAOI,cAAA,eAAA,aAAA,eAPJ,SAOI,cAAA,iBAAA,aAAA,iBAPJ,SAOI,cAAA,eAAA,aAAA,eAPJ,SAOI,YAAA,YAAA,eAAA,YAPJ,SAOI,YAAA,iBAAA,eAAA,iBAPJ,SAOI,YAAA,gBAAA,eAAA,gBAPJ,SAOI,YAAA,eAAA,eAAA,eAPJ,SAOI,YAAA,iBAAA,eAAA,iBAPJ,SAOI,YAAA,eAAA,eAAA,eAPJ,SAOI,YAAA,YAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,gBAPJ,SAOI,YAAA,eAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,eAPJ,SAOI,cAAA,YAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,gBAPJ,SAOI,cAAA,eAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,eAPJ,SAOI,eAAA,YAPJ,SAOI,eAAA,iBAPJ,SAOI,eAAA,gBAPJ,SAOI,eAAA,eAPJ,SAOI,eAAA,iBAPJ,SAOI,eAAA,eAPJ,SAOI,aAAA,YAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,gBAPJ,SAOI,aAAA,eAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,eAPJ,eAOI,WAAA,eAPJ,aAOI,WAAA,gBAPJ,gBAOI,WAAA,kBzDPR,yByDAI,gBAOI,MAAA,eAPJ,cAOI,MAAA,gBAPJ,eAOI,MAAA,eAPJ,aAOI,QAAA,iBAPJ,mBAOI,QAAA,uBAPJ,YAOI,QAAA,gBAPJ,WAOI,QAAA,eAPJ,YAOI,QAAA,gBAPJ,gBAOI,QAAA,oBAPJ,iBAOI,QAAA,qBAPJ,WAOI,QAAA,eAPJ,kBAOI,QAAA,sBAPJ,WAOI,QAAA,eAPJ,cAOI,KAAA,EAAA,EAAA,eAPJ,aAOI,eAAA,cAPJ,gBAOI,eAAA,iBAPJ,qBAOI,eAAA,sBAPJ,wBAOI,eAAA,yBAPJ,gBAOI,UAAA,YAPJ,gBAOI,UAAA,YAPJ,kBAOI,YAAA,YAPJ,kBAOI,YAAA,YAPJ,cAOI,UAAA,eAPJ,gBAOI,UAAA,iBAPJ,sBAOI,UAAA,uBAPJ,UAOI,IAAA,YAPJ,UAOI,IAAA,iBAPJ,UAOI,IAAA,gBAPJ,UAOI,IAAA,eAPJ,UAOI,IAAA,iBAPJ,UAOI,IAAA,eAPJ,0BAOI,gBAAA,qBAPJ,wBAOI,gBAAA,mBAPJ,2BAOI,gBAAA,iBAPJ,4BAOI,gBAAA,wBAPJ,2BAOI,gBAAA,uBAPJ,2BAOI,gBAAA,uBAPJ,sBAOI,YAAA,qBAPJ,oBAOI,YAAA,mBAPJ,uBAOI,YAAA,iBAPJ,yBAOI,YAAA,mBAPJ,wBAOI,YAAA,kBAPJ,wBAOI,cAAA,qBAPJ,sBAOI,cAAA,mBAPJ,yBAOI,cAAA,iBAPJ,0BAOI,cAAA,wBAPJ,yBAOI,cAAA,uBAPJ,0BAOI,cAAA,kBAPJ,oBAOI,WAAA,eAPJ,qBAOI,WAAA,qBAPJ,mBAOI,WAAA,mBAPJ,sBAOI,WAAA,iBAPJ,wBAOI,WAAA,mBAPJ,uBAOI,WAAA,kBAPJ,gBAOI,MAAA,aAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,eAOI,MAAA,YAPJ,QAOI,OAAA,YAPJ,QAOI,OAAA,iBAPJ,QAOI,OAAA,gBAPJ,QAOI,OAAA,eAPJ,QAOI,OAAA,iBAPJ,QAOI,OAAA,eAPJ,WAOI,OAAA,eAPJ,SAOI,aAAA,YAAA,YAAA,YAPJ,SAOI,aAAA,iBAAA,YAAA,iBAPJ,SAOI,aAAA,gBAAA,YAAA,gBAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,aAAA,iBAAA,YAAA,iBAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,YAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,WAAA,YAAA,cAAA,YAPJ,SAOI,WAAA,iBAAA,cAAA,iBAPJ,SAOI,WAAA,gBAAA,cAAA,gBAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,iBAAA,cAAA,iBAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,YAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,YAPJ,SAOI,WAAA,iBAPJ,SAOI,WAAA,gBAPJ,SAOI,WAAA,eAPJ,SAOI,WAAA,iBAPJ,SAOI,WAAA,eAPJ,YAOI,WAAA,eAPJ,SAOI,aAAA,YAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,gBAPJ,SAOI,aAAA,eAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,eAPJ,YAOI,aAAA,eAPJ,SAOI,cAAA,YAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,gBAPJ,SAOI,cAAA,eAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,eAPJ,YAOI,cAAA,eAPJ,SAOI,YAAA,YAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,gBAPJ,SAOI,YAAA,eAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,eAPJ,YAOI,YAAA,eAPJ,QAOI,QAAA,YAPJ,QAOI,QAAA,iBAPJ,QAOI,QAAA,gBAPJ,QAOI,QAAA,eAPJ,QAOI,QAAA,iBAPJ,QAOI,QAAA,eAPJ,SAOI,cAAA,YAAA,aAAA,YAPJ,SAOI,cAAA,iBAAA,aAAA,iBAPJ,SAOI,cAAA,gBAAA,aAAA,gBAPJ,SAOI,cAAA,eAAA,aAAA,eAPJ,SAOI,cAAA,iBAAA,aAAA,iBAPJ,SAOI,cAAA,eAAA,aAAA,eAPJ,SAOI,YAAA,YAAA,eAAA,YAPJ,SAOI,YAAA,iBAAA,eAAA,iBAPJ,SAOI,YAAA,gBAAA,eAAA,gBAPJ,SAOI,YAAA,eAAA,eAAA,eAPJ,SAOI,YAAA,iBAAA,eAAA,iBAPJ,SAOI,YAAA,eAAA,eAAA,eAPJ,SAOI,YAAA,YAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,gBAPJ,SAOI,YAAA,eAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,eAPJ,SAOI,cAAA,YAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,gBAPJ,SAOI,cAAA,eAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,eAPJ,SAOI,eAAA,YAPJ,SAOI,eAAA,iBAPJ,SAOI,eAAA,gBAPJ,SAOI,eAAA,eAPJ,SAOI,eAAA,iBAPJ,SAOI,eAAA,eAPJ,SAOI,aAAA,YAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,gBAPJ,SAOI,aAAA,eAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,eAPJ,eAOI,WAAA,eAPJ,aAOI,WAAA,gBAPJ,gBAOI,WAAA,kBzDPR,yByDAI,gBAOI,MAAA,eAPJ,cAOI,MAAA,gBAPJ,eAOI,MAAA,eAPJ,aAOI,QAAA,iBAPJ,mBAOI,QAAA,uBAPJ,YAOI,QAAA,gBAPJ,WAOI,QAAA,eAPJ,YAOI,QAAA,gBAPJ,gBAOI,QAAA,oBAPJ,iBAOI,QAAA,qBAPJ,WAOI,QAAA,eAPJ,kBAOI,QAAA,sBAPJ,WAOI,QAAA,eAPJ,cAOI,KAAA,EAAA,EAAA,eAPJ,aAOI,eAAA,cAPJ,gBAOI,eAAA,iBAPJ,qBAOI,eAAA,sBAPJ,wBAOI,eAAA,yBAPJ,gBAOI,UAAA,YAPJ,gBAOI,UAAA,YAPJ,kBAOI,YAAA,YAPJ,kBAOI,YAAA,YAPJ,cAOI,UAAA,eAPJ,gBAOI,UAAA,iBAPJ,sBAOI,UAAA,uBAPJ,UAOI,IAAA,YAPJ,UAOI,IAAA,iBAPJ,UAOI,IAAA,gBAPJ,UAOI,IAAA,eAPJ,UAOI,IAAA,iBAPJ,UAOI,IAAA,eAPJ,0BAOI,gBAAA,qBAPJ,wBAOI,gBAAA,mBAPJ,2BAOI,gBAAA,iBAPJ,4BAOI,gBAAA,wBAPJ,2BAOI,gBAAA,uBAPJ,2BAOI,gBAAA,uBAPJ,sBAOI,YAAA,qBAPJ,oBAOI,YAAA,mBAPJ,uBAOI,YAAA,iBAPJ,yBAOI,YAAA,mBAPJ,wBAOI,YAAA,kBAPJ,wBAOI,cAAA,qBAPJ,sBAOI,cAAA,mBAPJ,yBAOI,cAAA,iBAPJ,0BAOI,cAAA,wBAPJ,yBAOI,cAAA,uBAPJ,0BAOI,cAAA,kBAPJ,oBAOI,WAAA,eAPJ,qBAOI,WAAA,qBAPJ,mBAOI,WAAA,mBAPJ,sBAOI,WAAA,iBAPJ,wBAOI,WAAA,mBAPJ,uBAOI,WAAA,kBAPJ,gBAOI,MAAA,aAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,eAOI,MAAA,YAPJ,QAOI,OAAA,YAPJ,QAOI,OAAA,iBAPJ,QAOI,OAAA,gBAPJ,QAOI,OAAA,eAPJ,QAOI,OAAA,iBAPJ,QAOI,OAAA,eAPJ,WAOI,OAAA,eAPJ,SAOI,aAAA,YAAA,YAAA,YAPJ,SAOI,aAAA,iBAAA,YAAA,iBAPJ,SAOI,aAAA,gBAAA,YAAA,gBAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,aAAA,iBAAA,YAAA,iBAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,YAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,WAAA,YAAA,cAAA,YAPJ,SAOI,WAAA,iBAAA,cAAA,iBAPJ,SAOI,WAAA,gBAAA,cAAA,gBAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,iBAAA,cAAA,iBAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,YAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,YAPJ,SAOI,WAAA,iBAPJ,SAOI,WAAA,gBAPJ,SAOI,WAAA,eAPJ,SAOI,WAAA,iBAPJ,SAOI,WAAA,eAPJ,YAOI,WAAA,eAPJ,SAOI,aAAA,YAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,gBAPJ,SAOI,aAAA,eAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,eAPJ,YAOI,aAAA,eAPJ,SAOI,cAAA,YAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,gBAPJ,SAOI,cAAA,eAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,eAPJ,YAOI,cAAA,eAPJ,SAOI,YAAA,YAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,gBAPJ,SAOI,YAAA,eAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,eAPJ,YAOI,YAAA,eAPJ,QAOI,QAAA,YAPJ,QAOI,QAAA,iBAPJ,QAOI,QAAA,gBAPJ,QAOI,QAAA,eAPJ,QAOI,QAAA,iBAPJ,QAOI,QAAA,eAPJ,SAOI,cAAA,YAAA,aAAA,YAPJ,SAOI,cAAA,iBAAA,aAAA,iBAPJ,SAOI,cAAA,gBAAA,aAAA,gBAPJ,SAOI,cAAA,eAAA,aAAA,eAPJ,SAOI,cAAA,iBAAA,aAAA,iBAPJ,SAOI,cAAA,eAAA,aAAA,eAPJ,SAOI,YAAA,YAAA,eAAA,YAPJ,SAOI,YAAA,iBAAA,eAAA,iBAPJ,SAOI,YAAA,gBAAA,eAAA,gBAPJ,SAOI,YAAA,eAAA,eAAA,eAPJ,SAOI,YAAA,iBAAA,eAAA,iBAPJ,SAOI,YAAA,eAAA,eAAA,eAPJ,SAOI,YAAA,YAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,gBAPJ,SAOI,YAAA,eAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,eAPJ,SAOI,cAAA,YAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,gBAPJ,SAOI,cAAA,eAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,eAPJ,SAOI,eAAA,YAPJ,SAOI,eAAA,iBAPJ,SAOI,eAAA,gBAPJ,SAOI,eAAA,eAPJ,SAOI,eAAA,iBAPJ,SAOI,eAAA,eAPJ,SAOI,aAAA,YAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,gBAPJ,SAOI,aAAA,eAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,eAPJ,eAOI,WAAA,eAPJ,aAOI,WAAA,gBAPJ,gBAOI,WAAA,kBzDPR,0ByDAI,gBAOI,MAAA,eAPJ,cAOI,MAAA,gBAPJ,eAOI,MAAA,eAPJ,aAOI,QAAA,iBAPJ,mBAOI,QAAA,uBAPJ,YAOI,QAAA,gBAPJ,WAOI,QAAA,eAPJ,YAOI,QAAA,gBAPJ,gBAOI,QAAA,oBAPJ,iBAOI,QAAA,qBAPJ,WAOI,QAAA,eAPJ,kBAOI,QAAA,sBAPJ,WAOI,QAAA,eAPJ,cAOI,KAAA,EAAA,EAAA,eAPJ,aAOI,eAAA,cAPJ,gBAOI,eAAA,iBAPJ,qBAOI,eAAA,sBAPJ,wBAOI,eAAA,yBAPJ,gBAOI,UAAA,YAPJ,gBAOI,UAAA,YAPJ,kBAOI,YAAA,YAPJ,kBAOI,YAAA,YAPJ,cAOI,UAAA,eAPJ,gBAOI,UAAA,iBAPJ,sBAOI,UAAA,uBAPJ,UAOI,IAAA,YAPJ,UAOI,IAAA,iBAPJ,UAOI,IAAA,gBAPJ,UAOI,IAAA,eAPJ,UAOI,IAAA,iBAPJ,UAOI,IAAA,eAPJ,0BAOI,gBAAA,qBAPJ,wBAOI,gBAAA,mBAPJ,2BAOI,gBAAA,iBAPJ,4BAOI,gBAAA,wBAPJ,2BAOI,gBAAA,uBAPJ,2BAOI,gBAAA,uBAPJ,sBAOI,YAAA,qBAPJ,oBAOI,YAAA,mBAPJ,uBAOI,YAAA,iBAPJ,yBAOI,YAAA,mBAPJ,wBAOI,YAAA,kBAPJ,wBAOI,cAAA,qBAPJ,sBAOI,cAAA,mBAPJ,yBAOI,cAAA,iBAPJ,0BAOI,cAAA,wBAPJ,yBAOI,cAAA,uBAPJ,0BAOI,cAAA,kBAPJ,oBAOI,WAAA,eAPJ,qBAOI,WAAA,qBAPJ,mBAOI,WAAA,mBAPJ,sBAOI,WAAA,iBAPJ,wBAOI,WAAA,mBAPJ,uBAOI,WAAA,kBAPJ,gBAOI,MAAA,aAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,eAOI,MAAA,YAPJ,QAOI,OAAA,YAPJ,QAOI,OAAA,iBAPJ,QAOI,OAAA,gBAPJ,QAOI,OAAA,eAPJ,QAOI,OAAA,iBAPJ,QAOI,OAAA,eAPJ,WAOI,OAAA,eAPJ,SAOI,aAAA,YAAA,YAAA,YAPJ,SAOI,aAAA,iBAAA,YAAA,iBAPJ,SAOI,aAAA,gBAAA,YAAA,gBAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,aAAA,iBAAA,YAAA,iBAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,YAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,WAAA,YAAA,cAAA,YAPJ,SAOI,WAAA,iBAAA,cAAA,iBAPJ,SAOI,WAAA,gBAAA,cAAA,gBAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,iBAAA,cAAA,iBAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,YAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,YAPJ,SAOI,WAAA,iBAPJ,SAOI,WAAA,gBAPJ,SAOI,WAAA,eAPJ,SAOI,WAAA,iBAPJ,SAOI,WAAA,eAPJ,YAOI,WAAA,eAPJ,SAOI,aAAA,YAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,gBAPJ,SAOI,aAAA,eAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,eAPJ,YAOI,aAAA,eAPJ,SAOI,cAAA,YAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,gBAPJ,SAOI,cAAA,eAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,eAPJ,YAOI,cAAA,eAPJ,SAOI,YAAA,YAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,gBAPJ,SAOI,YAAA,eAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,eAPJ,YAOI,YAAA,eAPJ,QAOI,QAAA,YAPJ,QAOI,QAAA,iBAPJ,QAOI,QAAA,gBAPJ,QAOI,QAAA,eAPJ,QAOI,QAAA,iBAPJ,QAOI,QAAA,eAPJ,SAOI,cAAA,YAAA,aAAA,YAPJ,SAOI,cAAA,iBAAA,aAAA,iBAPJ,SAOI,cAAA,gBAAA,aAAA,gBAPJ,SAOI,cAAA,eAAA,aAAA,eAPJ,SAOI,cAAA,iBAAA,aAAA,iBAPJ,SAOI,cAAA,eAAA,aAAA,eAPJ,SAOI,YAAA,YAAA,eAAA,YAPJ,SAOI,YAAA,iBAAA,eAAA,iBAPJ,SAOI,YAAA,gBAAA,eAAA,gBAPJ,SAOI,YAAA,eAAA,eAAA,eAPJ,SAOI,YAAA,iBAAA,eAAA,iBAPJ,SAOI,YAAA,eAAA,eAAA,eAPJ,SAOI,YAAA,YAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,gBAPJ,SAOI,YAAA,eAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,eAPJ,SAOI,cAAA,YAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,gBAPJ,SAOI,cAAA,eAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,eAPJ,SAOI,eAAA,YAPJ,SAOI,eAAA,iBAPJ,SAOI,eAAA,gBAPJ,SAOI,eAAA,eAPJ,SAOI,eAAA,iBAPJ,SAOI,eAAA,eAPJ,SAOI,aAAA,YAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,gBAPJ,SAOI,aAAA,eAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,eAPJ,eAOI,WAAA,eAPJ,aAOI,WAAA,gBAPJ,gBAOI,WAAA,kBzDPR,0ByDAI,iBAOI,MAAA,eAPJ,eAOI,MAAA,gBAPJ,gBAOI,MAAA,eAPJ,cAOI,QAAA,iBAPJ,oBAOI,QAAA,uBAPJ,aAOI,QAAA,gBAPJ,YAOI,QAAA,eAPJ,aAOI,QAAA,gBAPJ,iBAOI,QAAA,oBAPJ,kBAOI,QAAA,qBAPJ,YAOI,QAAA,eAPJ,mBAOI,QAAA,sBAPJ,YAOI,QAAA,eAPJ,eAOI,KAAA,EAAA,EAAA,eAPJ,cAOI,eAAA,cAPJ,iBAOI,eAAA,iBAPJ,sBAOI,eAAA,sBAPJ,yBAOI,eAAA,yBAPJ,iBAOI,UAAA,YAPJ,iBAOI,UAAA,YAPJ,mBAOI,YAAA,YAPJ,mBAOI,YAAA,YAPJ,eAOI,UAAA,eAPJ,iBAOI,UAAA,iBAPJ,uBAOI,UAAA,uBAPJ,WAOI,IAAA,YAPJ,WAOI,IAAA,iBAPJ,WAOI,IAAA,gBAPJ,WAOI,IAAA,eAPJ,WAOI,IAAA,iBAPJ,WAOI,IAAA,eAPJ,2BAOI,gBAAA,qBAPJ,yBAOI,gBAAA,mBAPJ,4BAOI,gBAAA,iBAPJ,6BAOI,gBAAA,wBAPJ,4BAOI,gBAAA,uBAPJ,4BAOI,gBAAA,uBAPJ,uBAOI,YAAA,qBAPJ,qBAOI,YAAA,mBAPJ,wBAOI,YAAA,iBAPJ,0BAOI,YAAA,mBAPJ,yBAOI,YAAA,kBAPJ,yBAOI,cAAA,qBAPJ,uBAOI,cAAA,mBAPJ,0BAOI,cAAA,iBAPJ,2BAOI,cAAA,wBAPJ,0BAOI,cAAA,uBAPJ,2BAOI,cAAA,kBAPJ,qBAOI,WAAA,eAPJ,sBAOI,WAAA,qBAPJ,oBAOI,WAAA,mBAPJ,uBAOI,WAAA,iBAPJ,yBAOI,WAAA,mBAPJ,wBAOI,WAAA,kBAPJ,iBAOI,MAAA,aAPJ,aAOI,MAAA,YAPJ,aAOI,MAAA,YAPJ,aAOI,MAAA,YAPJ,aAOI,MAAA,YAPJ,aAOI,MAAA,YAPJ,aAOI,MAAA,YAPJ,gBAOI,MAAA,YAPJ,SAOI,OAAA,YAPJ,SAOI,OAAA,iBAPJ,SAOI,OAAA,gBAPJ,SAOI,OAAA,eAPJ,SAOI,OAAA,iBAPJ,SAOI,OAAA,eAPJ,YAOI,OAAA,eAPJ,UAOI,aAAA,YAAA,YAAA,YAPJ,UAOI,aAAA,iBAAA,YAAA,iBAPJ,UAOI,aAAA,gBAAA,YAAA,gBAPJ,UAOI,aAAA,eAAA,YAAA,eAPJ,UAOI,aAAA,iBAAA,YAAA,iBAPJ,UAOI,aAAA,eAAA,YAAA,eAPJ,aAOI,aAAA,eAAA,YAAA,eAPJ,UAOI,WAAA,YAAA,cAAA,YAPJ,UAOI,WAAA,iBAAA,cAAA,iBAPJ,UAOI,WAAA,gBAAA,cAAA,gBAPJ,UAOI,WAAA,eAAA,cAAA,eAPJ,UAOI,WAAA,iBAAA,cAAA,iBAPJ,UAOI,WAAA,eAAA,cAAA,eAPJ,aAOI,WAAA,eAAA,cAAA,eAPJ,UAOI,WAAA,YAPJ,UAOI,WAAA,iBAPJ,UAOI,WAAA,gBAPJ,UAOI,WAAA,eAPJ,UAOI,WAAA,iBAPJ,UAOI,WAAA,eAPJ,aAOI,WAAA,eAPJ,UAOI,aAAA,YAPJ,UAOI,aAAA,iBAPJ,UAOI,aAAA,gBAPJ,UAOI,aAAA,eAPJ,UAOI,aAAA,iBAPJ,UAOI,aAAA,eAPJ,aAOI,aAAA,eAPJ,UAOI,cAAA,YAPJ,UAOI,cAAA,iBAPJ,UAOI,cAAA,gBAPJ,UAOI,cAAA,eAPJ,UAOI,cAAA,iBAPJ,UAOI,cAAA,eAPJ,aAOI,cAAA,eAPJ,UAOI,YAAA,YAPJ,UAOI,YAAA,iBAPJ,UAOI,YAAA,gBAPJ,UAOI,YAAA,eAPJ,UAOI,YAAA,iBAPJ,UAOI,YAAA,eAPJ,aAOI,YAAA,eAPJ,SAOI,QAAA,YAPJ,SAOI,QAAA,iBAPJ,SAOI,QAAA,gBAPJ,SAOI,QAAA,eAPJ,SAOI,QAAA,iBAPJ,SAOI,QAAA,eAPJ,UAOI,cAAA,YAAA,aAAA,YAPJ,UAOI,cAAA,iBAAA,aAAA,iBAPJ,UAOI,cAAA,gBAAA,aAAA,gBAPJ,UAOI,cAAA,eAAA,aAAA,eAPJ,UAOI,cAAA,iBAAA,aAAA,iBAPJ,UAOI,cAAA,eAAA,aAAA,eAPJ,UAOI,YAAA,YAAA,eAAA,YAPJ,UAOI,YAAA,iBAAA,eAAA,iBAPJ,UAOI,YAAA,gBAAA,eAAA,gBAPJ,UAOI,YAAA,eAAA,eAAA,eAPJ,UAOI,YAAA,iBAAA,eAAA,iBAPJ,UAOI,YAAA,eAAA,eAAA,eAPJ,UAOI,YAAA,YAPJ,UAOI,YAAA,iBAPJ,UAOI,YAAA,gBAPJ,UAOI,YAAA,eAPJ,UAOI,YAAA,iBAPJ,UAOI,YAAA,eAPJ,UAOI,cAAA,YAPJ,UAOI,cAAA,iBAPJ,UAOI,cAAA,gBAPJ,UAOI,cAAA,eAPJ,UAOI,cAAA,iBAPJ,UAOI,cAAA,eAPJ,UAOI,eAAA,YAPJ,UAOI,eAAA,iBAPJ,UAOI,eAAA,gBAPJ,UAOI,eAAA,eAPJ,UAOI,eAAA,iBAPJ,UAOI,eAAA,eAPJ,UAOI,aAAA,YAPJ,UAOI,aAAA,iBAPJ,UAOI,aAAA,gBAPJ,UAOI,aAAA,eAPJ,UAOI,aAAA,iBAPJ,UAOI,aAAA,eAPJ,gBAOI,WAAA,eAPJ,cAOI,WAAA,gBAPJ,iBAOI,WAAA,kBCnDZ,0BD4CQ,MAOI,UAAA,iBAPJ,MAOI,UAAA,eAPJ,MAOI,UAAA,kBAPJ,MAOI,UAAA,kBChCZ,aDyBQ,gBAOI,QAAA,iBAPJ,sBAOI,QAAA,uBAPJ,eAOI,QAAA,gBAPJ,cAOI,QAAA,eAPJ,eAOI,QAAA,gBAPJ,mBAOI,QAAA,oBAPJ,oBAOI,QAAA,qBAPJ,cAOI,QAAA,eAPJ,qBAOI,QAAA,sBAPJ,cAOI,QAAA","sourcesContent":["/*!\n * Bootstrap v5.1.0 (https://getbootstrap.com/)\n * Copyright 2011-2021 The Bootstrap Authors\n * Copyright 2011-2021 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n\n// scss-docs-start import-stack\n// Configuration\n@import \"functions\";\n@import \"variables\";\n@import \"mixins\";\n@import \"utilities\";\n\n// Layout & components\n@import \"root\";\n@import \"reboot\";\n@import \"type\";\n@import \"images\";\n@import \"containers\";\n@import \"grid\";\n@import \"tables\";\n@import \"forms\";\n@import \"buttons\";\n@import \"transitions\";\n@import \"dropdown\";\n@import \"button-group\";\n@import \"nav\";\n@import \"navbar\";\n@import \"card\";\n@import \"accordion\";\n@import \"breadcrumb\";\n@import \"pagination\";\n@import \"badge\";\n@import \"alert\";\n@import \"progress\";\n@import \"list-group\";\n@import \"close\";\n@import \"toasts\";\n@import \"modal\";\n@import \"tooltip\";\n@import \"popover\";\n@import \"carousel\";\n@import \"spinners\";\n@import \"offcanvas\";\n@import \"placeholders\";\n\n// Helpers\n@import \"helpers\";\n\n// Utilities\n@import \"utilities/api\";\n// scss-docs-end import-stack\n",":root {\n // Note: Custom variable values only support SassScript inside `#{}`.\n\n // Colors\n //\n // Generate palettes for full colors, grays, and theme colors.\n\n @each $color, $value in $colors {\n --#{$variable-prefix}#{$color}: #{$value};\n }\n\n @each $color, $value in $grays {\n --#{$variable-prefix}gray-#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors {\n --#{$variable-prefix}#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors-rgb {\n --#{$variable-prefix}#{$color}-rgb: #{$value};\n }\n\n --#{$variable-prefix}white-rgb: #{to-rgb($white)};\n --#{$variable-prefix}black-rgb: #{to-rgb($black)};\n --#{$variable-prefix}body-rgb: #{to-rgb($body-color)};\n\n // Fonts\n\n // Note: Use `inspect` for lists so that quoted items keep the quotes.\n // See https://github.com/sass/sass/issues/2383#issuecomment-336349172\n --#{$variable-prefix}font-sans-serif: #{inspect($font-family-sans-serif)};\n --#{$variable-prefix}font-monospace: #{inspect($font-family-monospace)};\n --#{$variable-prefix}gradient: #{$gradient};\n\n // Root and body\n // stylelint-disable custom-property-empty-line-before\n // scss-docs-start root-body-variables\n @if $font-size-root != null {\n --#{$variable-prefix}root-font-size: #{$font-size-root};\n }\n --#{$variable-prefix}body-font-family: #{$font-family-base};\n --#{$variable-prefix}body-font-size: #{$font-size-base};\n --#{$variable-prefix}body-font-weight: #{$font-weight-base};\n --#{$variable-prefix}body-line-height: #{$line-height-base};\n --#{$variable-prefix}body-color: #{$body-color};\n @if $body-text-align != null {\n --#{$variable-prefix}body-text-align: #{$body-text-align};\n }\n --#{$variable-prefix}body-bg: #{$body-bg};\n // scss-docs-end root-body-variables\n // stylelint-enable custom-property-empty-line-before\n}\n","// stylelint-disable declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\n\n\n// Reboot\n//\n// Normalization of HTML elements, manually forked from Normalize.css to remove\n// styles targeting irrelevant browsers while applying new styles.\n//\n// Normalize is licensed MIT. https://github.com/necolas/normalize.css\n\n\n// Document\n//\n// Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n\n// Root\n//\n// Ability to the value of the root font sizes, affecting the value of `rem`.\n// null by default, thus nothing is generated.\n\n:root {\n @if $font-size-root != null {\n font-size: var(--#{$variable-prefix}-root-font-size);\n }\n\n @if $enable-smooth-scroll {\n @media (prefers-reduced-motion: no-preference) {\n scroll-behavior: smooth;\n }\n }\n}\n\n\n// Body\n//\n// 1. Remove the margin in all browsers.\n// 2. As a best practice, apply a default `background-color`.\n// 3. Prevent adjustments of font size after orientation changes in iOS.\n// 4. Change the default tap highlight to be completely transparent in iOS.\n\n// scss-docs-start reboot-body-rules\nbody {\n margin: 0; // 1\n font-family: var(--#{$variable-prefix}body-font-family);\n @include font-size(var(--#{$variable-prefix}body-font-size));\n font-weight: var(--#{$variable-prefix}body-font-weight);\n line-height: var(--#{$variable-prefix}body-line-height);\n color: var(--#{$variable-prefix}body-color);\n text-align: var(--#{$variable-prefix}body-text-align);\n background-color: var(--#{$variable-prefix}body-bg); // 2\n -webkit-text-size-adjust: 100%; // 3\n -webkit-tap-highlight-color: rgba($black, 0); // 4\n}\n// scss-docs-end reboot-body-rules\n\n\n// Content grouping\n//\n// 1. Reset Firefox's gray color\n// 2. Set correct height and prevent the `size` attribute to make the `hr` look like an input field\n\nhr {\n margin: $hr-margin-y 0;\n color: $hr-color; // 1\n background-color: currentColor;\n border: 0;\n opacity: $hr-opacity;\n}\n\nhr:not([size]) {\n height: $hr-height; // 2\n}\n\n\n// Typography\n//\n// 1. Remove top margins from headings\n// By default, `

`-`

` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n\n%heading {\n margin-top: 0; // 1\n margin-bottom: $headings-margin-bottom;\n font-family: $headings-font-family;\n font-style: $headings-font-style;\n font-weight: $headings-font-weight;\n line-height: $headings-line-height;\n color: $headings-color;\n}\n\nh1 {\n @extend %heading;\n @include font-size($h1-font-size);\n}\n\nh2 {\n @extend %heading;\n @include font-size($h2-font-size);\n}\n\nh3 {\n @extend %heading;\n @include font-size($h3-font-size);\n}\n\nh4 {\n @extend %heading;\n @include font-size($h4-font-size);\n}\n\nh5 {\n @extend %heading;\n @include font-size($h5-font-size);\n}\n\nh6 {\n @extend %heading;\n @include font-size($h6-font-size);\n}\n\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\n\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n\n// Abbreviations\n//\n// 1. Duplicate behavior to the data-bs-* attribute for our tooltip plugin\n// 2. Add the correct text decoration in Chrome, Edge, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Prevent the text-decoration to be skipped.\n\nabbr[title],\nabbr[data-bs-original-title] { // 1\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n text-decoration-skip-ink: none; // 4\n}\n\n\n// Address\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\n\n// Lists\n\nol,\nul {\n padding-left: 2rem;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\n// 1. Undo browser default\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // 1\n}\n\n\n// Blockquote\n\nblockquote {\n margin: 0 0 1rem;\n}\n\n\n// Strong\n//\n// Add the correct font weight in Chrome, Edge, and Safari\n\nb,\nstrong {\n font-weight: $font-weight-bolder;\n}\n\n\n// Small\n//\n// Add the correct font size in all browsers\n\nsmall {\n @include font-size($small-font-size);\n}\n\n\n// Mark\n\nmark {\n padding: $mark-padding;\n background-color: $mark-bg;\n}\n\n\n// Sub and Sup\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n\nsub,\nsup {\n position: relative;\n @include font-size($sub-sup-font-size);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n// Links\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n\n &:hover {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]):not([class]) {\n &,\n &:hover {\n color: inherit;\n text-decoration: none;\n }\n}\n\n\n// Code\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-code;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n direction: ltr #{\"/* rtl:ignore */\"};\n unicode-bidi: bidi-override;\n}\n\n// 1. Remove browser default top margin\n// 2. Reset browser default of `1em` to use `rem`s\n// 3. Don't allow content to break outside\n\npre {\n display: block;\n margin-top: 0; // 1\n margin-bottom: 1rem; // 2\n overflow: auto; // 3\n @include font-size($code-font-size);\n color: $pre-color;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n @include font-size(inherit);\n color: inherit;\n word-break: normal;\n }\n}\n\ncode {\n @include font-size($code-font-size);\n color: $code-color;\n word-wrap: break-word;\n\n // Streamline the style when inside anchors to avoid broken underline and more\n a > & {\n color: inherit;\n }\n}\n\nkbd {\n padding: $kbd-padding-y $kbd-padding-x;\n @include font-size($kbd-font-size);\n color: $kbd-color;\n background-color: $kbd-bg;\n @include border-radius($border-radius-sm);\n\n kbd {\n padding: 0;\n @include font-size(1em);\n font-weight: $nested-kbd-font-weight;\n }\n}\n\n\n// Figures\n//\n// Apply a consistent margin strategy (matches our type styles).\n\nfigure {\n margin: 0 0 1rem;\n}\n\n\n// Images and content\n\nimg,\nsvg {\n vertical-align: middle;\n}\n\n\n// Tables\n//\n// Prevent double borders\n\ntable {\n caption-side: bottom;\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: $table-cell-padding-y;\n padding-bottom: $table-cell-padding-y;\n color: $table-caption-color;\n text-align: left;\n}\n\n// 1. Removes font-weight bold by inheriting\n// 2. Matches default `` alignment by inheriting `text-align`.\n// 3. Fix alignment for Safari\n\nth {\n font-weight: $table-th-font-weight; // 1\n text-align: inherit; // 2\n text-align: -webkit-match-parent; // 3\n}\n\nthead,\ntbody,\ntfoot,\ntr,\ntd,\nth {\n border-color: inherit;\n border-style: solid;\n border-width: 0;\n}\n\n\n// Forms\n//\n// 1. Allow labels to use `margin` for spacing.\n\nlabel {\n display: inline-block; // 1\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n// See https://github.com/twbs/bootstrap/issues/24093\n\nbutton {\n // stylelint-disable-next-line property-disallowed-list\n border-radius: 0;\n}\n\n// Explicitly remove focus outline in Chromium when it shouldn't be\n// visible (e.g. as result of mouse click or touch tap). It already\n// should be doing this automatically, but seems to currently be\n// confused and applies its very visible two-tone outline anyway.\n\nbutton:focus:not(:focus-visible) {\n outline: 0;\n}\n\n// 1. Remove the margin in Firefox and Safari\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // 1\n font-family: inherit;\n @include font-size(inherit);\n line-height: inherit;\n}\n\n// Remove the inheritance of text transform in Firefox\nbutton,\nselect {\n text-transform: none;\n}\n// Set the cursor for non-`