diff --git a/.github/readme/README_en.md b/.github/readme/README_en.md
index d8a8fd5b..562d0220 100644
--- a/.github/readme/README_en.md
+++ b/.github/readme/README_en.md
@@ -16,6 +16,9 @@ Open source manga and ranobe reading application
- Available in 🇷🇺Russian 🇺🇦Ukrainian and 🇬🇧English languages
## Currently supported catalogs
+ Anime:
+ - Shikimori (🇷🇺)
+ - AniLib (🇷🇺)
Manga:
- Desu (🇷🇺)
@@ -28,8 +31,10 @@ Open source manga and ranobe reading application
- Rulate (🇷🇺)
- Erolate (🇷🇺)
- Ranobehub (🇷🇺)
+ - RanobeLib (🇷🇺)
Hentai Manga:
+ - AllHentai (🇷🇺)
- NHentai (🇬🇧)
## Screenshots
diff --git a/.github/readme/README_ru.md b/.github/readme/README_ru.md
index 8b762cbf..620f7102 100644
--- a/.github/readme/README_ru.md
+++ b/.github/readme/README_ru.md
@@ -16,6 +16,9 @@
- Доступно на 🇷🇺Русском 🇺🇦Украинском и 🇬🇧Английском языках.
## Поддерживаемые каталоги
+ Аніме:
+ - Shikimori (🇷🇺)
+ - AniLib (🇷🇺)
Манга:
- Desu (🇷🇺)
@@ -28,8 +31,10 @@
- Rulate (🇷🇺)
- Erolate (🇷🇺)
- Ranobehub (🇷🇺)
+ - RanobeLib (🇷🇺)
Хентай манга:
+ - AllHentai (🇷🇺)
- NHentai (🇬🇧)
## Скриншоты
diff --git a/.github/readme/README_uk.md b/.github/readme/README_uk.md
index e7689545..e252524d 100644
--- a/.github/readme/README_uk.md
+++ b/.github/readme/README_uk.md
@@ -16,6 +16,9 @@
- Доступно на 🇷🇺Російською 🇺🇦Українською та 🇬🇧Англійською мовами.
## Підтримувані каталоги
+ Аниме:
+ - Shikimori (🇷🇺)
+ - AniLib (🇷🇺)
Манга:
- Desu (🇷🇺)
@@ -28,8 +31,10 @@
- Rulate (🇷🇺)
- Erolate (🇷🇺)
- Ranobehub (🇷🇺)
+ - RanobeLib (🇷🇺)
Хентай манга:
+ - AllHentai (🇷🇺)
- NHentai (🇬🇧)
## Скріншоти
diff --git a/README.md b/README.md
index 6f4e93af..855063a8 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,9 @@ Open source manga and ranobe reading application
- Available in 🇷🇺Russian 🇺🇦Ukrainian and 🇬🇧English languages
## Currently supported catalogs
+ Anime:
+ - Shikimori (🇷🇺)
+ - AniLib (🇷🇺)
Manga:
- Desu (🇷🇺)
@@ -29,8 +32,10 @@ Open source manga and ranobe reading application
- Rulate (🇷🇺)
- Erolate (🇷🇺)
- Ranobehub (🇷🇺)
+ - RanobeLib (🇷🇺)
Hentai Manga:
+ - AllHentai (🇷🇺)
- NHentai (🇬🇧)
## Screenshots
diff --git a/data/i18n/en_US.qm b/data/i18n/en_US.qm
new file mode 100644
index 00000000..6782cac6
Binary files /dev/null and b/data/i18n/en_US.qm differ
diff --git a/data/i18n/en_US.ts b/data/i18n/en_US.ts
new file mode 100644
index 00000000..ddf1b790
--- /dev/null
+++ b/data/i18n/en_US.ts
@@ -0,0 +1,442 @@
+
+
+
+
+ Dialog
+
+
+ Show spoilers
+
+
+
+ Cancel
+
+
+
+ Delete
+
+
+
+ Update
+
+
+
+ List
+
+
+
+ Chapters read
+
+
+
+ Rating
+
+
+
+ Get code
+
+
+
+ Authorization code
+
+
+
+ Login
+
+
+
+ Password
+
+
+
+ Sign in
+
+
+
+ Form
+
+
+ Nlight
+
+
+
+ Kind
+
+
+
+ Order
+
+
+
+ Apply
+
+
+
+ Reset
+
+
+
+ Filters
+
+
+
+ Genres list
+
+
+
+ Catalogs
+
+
+
+ Sign in
+
+
+
+ Search
+
+
+
+ Related
+
+
+
+ Characters
+
+
+
+ Planned
+
+
+
+ Completed
+
+
+
+ Reading
+
+
+
+ Re-reading
+
+
+
+ On hold
+
+
+
+ Dropped
+
+
+
+ MainWindow
+
+
+ Nlight
+
+
+
+ Main
+
+
+
+ Library
+
+
+
+ History
+
+
+
+ Shikimori
+
+
+
+ Menu
+
+
+ Open in browser
+
+
+
+ Remove from library
+
+
+
+ Add to Library
+
+
+
+ Mark as read
+
+
+
+ Remove all
+
+
+
+ Mark as read all previous
+
+
+
+ Remove read mark
+
+
+
+ Clear local files
+
+
+
+ Open local files
+
+
+
+ Other
+
+
+ Page
+
+
+
+ Sign in
+
+
+
+ Status
+
+
+
+ Volumes
+
+
+
+ Chapters
+
+
+
+ Rating
+
+
+
+ Page is loading
+
+
+
+ Status
+
+
+ Ongoing
+
+
+
+ Released
+
+
+
+ Frozen
+
+
+
+ NlLanguage
+
+
+ Russian
+
+
+
+ Ukrainian
+
+
+
+ Japanese
+
+
+
+ English
+
+
+
+ Undefined
+
+
+
+ NlKind
+
+
+ Manga
+
+
+
+ OEL-manga
+
+
+
+ Rumanga
+
+
+
+ Manhwa
+
+
+
+ Manhua
+
+
+
+ Oneshot
+
+
+
+ Comic
+
+
+
+ Western comic
+
+
+
+ Rucomic
+
+
+
+ Indonesian comic
+
+
+
+ Doujin
+
+
+
+ Ranobe
+
+
+
+ Other
+
+
+
+ Undefined
+
+
+
+ Message
+
+
+ Manga {} has been added.
+
+
+
+ Manga {} has been deleted.
+
+
+
+ Files {} have been removed.
+
+
+
+ Check for updates.
+
+
+
+ Error checking for updates.
+
+
+
+ No updates available. You are using the latest version.
+
+
+
+ New version {result} is available! You are currently on version {APP_VERSION}.
+
+
+
+ No connection
+
+
+
+ Nothing found
+
+
+
+ SettingsInterface
+
+
+ Use system setting
+
+
+
+ Interface zoom
+
+
+
+ Change the size of widgets and fonts
+
+
+
+ Language
+
+
+
+ Set your preferred language for UI
+
+
+
+ Software update
+
+
+
+ Check for updates when the application starts
+
+
+
+ The new version will be more stable and have more features
+
+
+
+ Changes will take effect after restarting the application
+
+
+
+ Check for updates
+
+
+
+ Settings
+
+
+
+ Application theme
+
+
+
+ Change the appearance of application
+
+
+
+ Dark
+
+
+
+ Light
+
+
+
+ Personalization
+
+
+
+ About
+
+
+
+ Project on GitHub
+
+
+
+ Episodes
+
+
+
+ Automatically mark episodes as watched
+
+
+
\ No newline at end of file
diff --git a/data/i18n/ru_RU.qm b/data/i18n/ru_RU.qm
new file mode 100644
index 00000000..f028649f
Binary files /dev/null and b/data/i18n/ru_RU.qm differ
diff --git a/data/translations/ru/ru b/data/i18n/ru_RU.ts
similarity index 70%
rename from data/translations/ru/ru
rename to data/i18n/ru_RU.ts
index d3e9ca7a..11e7b58d 100644
--- a/data/translations/ru/ru
+++ b/data/i18n/ru_RU.ts
@@ -1,6 +1,6 @@
-
+
Dialog
@@ -90,6 +90,10 @@
Войти
+
+
+ Поиск
+
Связанное
@@ -223,9 +227,13 @@
Выходит
-
+
Издано
+
+
+ Заморожено
+
NlLanguage
@@ -331,9 +339,104 @@
Ошибка проверки обновлений.
+
+
+ Нет доступных обновлений. Вы используете последнюю версию.
+
Новая версия {result} доступна! Вы используете версию {APP_VERSION}.
+
+
+ Нет соединения
+
+
+
+ Ничего не найдено
+
+
+
+ SettingsInterface
+
+
+ Использовать системные настройки
+
+
+
+ Масштабирование
+
+
+
+ Изменение размера виджетов и шрифтов
+
+
+
+ Язык
+
+
+
+ Установите предпочитаемый язык пользовательского интерфейса
+
+
+
+ Обновления
+
+
+
+ Проверять наличие обновлений при запуске приложения
+
+
+
+ Новая версия будет более стабильной и будет иметь больше возможностей
+
+
+
+ Изменения вступят в силу после перезапуска приложения
+
+
+
+ Проверить обновления
+
+
+
+ Параметры
+
+
+
+ Тема приложения
+
+
+
+ Изменить внешний вид приложения
+
+
+
+ Темная
+
+
+
+ Светлая
+
+
+
+ Персонализация
+
+
+
+ О проекте
+
+
+
+ Проект на GitHub
+
+
+
+ Эпизоды
+
+
+
+ Автоматически отмечать эпизоды как просмотренные
+
-
+
\ No newline at end of file
diff --git a/data/i18n/uk_UA.qm b/data/i18n/uk_UA.qm
new file mode 100644
index 00000000..1b2d9db5
Binary files /dev/null and b/data/i18n/uk_UA.qm differ
diff --git a/data/translations/uk/uk b/data/i18n/uk_UA.ts
similarity index 71%
rename from data/translations/uk/uk
rename to data/i18n/uk_UA.ts
index 26ed06f4..eb7e6d27 100644
--- a/data/translations/uk/uk
+++ b/data/i18n/uk_UA.ts
@@ -1,6 +1,6 @@
-
+
Dialog
@@ -90,6 +90,10 @@
Увійти
+
+
+ Пошук
+
Пов'язане
@@ -223,9 +227,13 @@
Виходить
-
+
Видано
+
+
+ Заморожено
+
NlLanguage
@@ -331,9 +339,104 @@
Помилка перевірки оновлень.
+
+
+ Немає доступних оновлень. Ви використовуєте останню версію.
+
Нова версія {result} доступна! Ви використовуєте версію {APP_VERSION}.
+
+
+ Немає з'єднання
+
+
+
+ Нічого не знайдено
+
+
+
+ SettingsInterface
+
+
+ Використовувати системні налаштування
+
+
+
+ Масштабування
+
+
+
+ Зміна розміру віджетів та шрифтів
+
+
+
+ Мова
+
+
+
+ Встановіть бажану мову інтерфейсу користувача
+
+
+
+ Оновлення
+
+
+
+ Перевірити наявність оновлень під час запуску програми
+
+
+
+ Нова версія буде більш стабільною і матиме більше можливостей
+
+
+
+ Зміни набудуть чинності після перезапуску програми
+
+
+
+ Перевірити оновлення
+
+
+
+ Параметри
+
+
+
+ Тема програми
+
+
+
+ Змінити зовнішній вигляд програми
+
+
+
+ Темна
+
+
+
+ Світла
+
+
+
+ Персоналізація
+
+
+
+ Про проект
+
+
+
+ Проект на GitHub
+
+
+
+ Епізоди
+
+
+
+ Автоматично позначати епізоди як переглянуті
+
-
+
\ No newline at end of file
diff --git a/data/resource.py b/data/resource.py
new file mode 100644
index 00000000..86420e47
--- /dev/null
+++ b/data/resource.py
@@ -0,0 +1,2258 @@
+# Resource object code (Python 3)
+# Created by: object code
+# Created by: The Resource Compiler for Qt version 6.7.2
+# WARNING! All changes made in this file will be lost!
+
+from PySide6 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x03\x8a\
+<\
+svg xmlns=\x22http:\
+//www.w3.org/200\
+0/svg\x22 width=\x2240\
+\x22 height=\x2240\x22 vi\
+ewBox=\x220 0 40 40\
+\x22>\x0d\x0a\x0d\x0a\x0d\x0a\x0d\x0a\x0d\x0a\x0d\
+\x0a\x0d\x0a\
+\x00\x00\x13#\
+\x89\
+PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
+\x00\x00\xfa\x00\x00\x00\xfa\x08\x06\x00\x00\x00\x88\xecZ=\
+\x00\x00\x12\xeaIDATx^\xed\x9di\x94U\xd5\
+\x95\x80w\xb1:\x0e\x0b\x13\x93\xb6W\x8c\xd14\x08\x88\
+\xca$\x0e\x88`\x89\x12\x09*\x0a\x8d\xb66bH\x1c\
+Iw\xd4\x12Q\x10\x11DD\x06\xc1\x01\x05Ke(\
+\xedD\x05A\xc60*j\x9cHc\xc4\x09$iM\
+kB\x96I$\x11\x89\x98\xc5\xeavE\xe8:\x1ac\
+QT\xd5\xdbw\xdfs\xa7w\xbe\xbb\x16\xbf\xdc{\x9f\
+\xbb\xbf}>OU\xbdw\xdf\xab\xd8\xbby\x9f]\xc2\
+\x05\x01\x08\x945\x81\x0aD/\xeb\xf9\xd2\x1c\x04>%\
+\x80\xe8l\x04\x08\x04@\x00\xd1\x03\x182-B\x00\xd1\
+\xd9\x03\x10\x08\x80\x00\xa2\x070dZ\x84\x00\xa2\xb3\x07\
+ \x10\x00\x01D\x0f`\xc8\xb4\x08\x01Dg\x0f@ \
+\x00\x02\x88\x1e\xc0\x90i\x11\x02\x88\xce\x1e\x80@\x00\x04\
+\x10=\x80!\xd3\x22\x04\x10\x9d=\x00\x81\x00\x08 z\
+\x00C\xa6E\x08 :{\x00\x02\x01\x10@\xf4\x00\x86\
+L\x8b\x10@t\xf6\x00\x04\x02 \x80\xe8\x01\x0c\x99\x16\
+!\x80\xe8\xec\x01\x08\x04@\x00\xd1\x03\x182-B\x00\
+\xd1\xd9\x03\x10\x08\x80\x00\xa2\x070dZ\x84\x00\xa2\xb3\
+\x07 \x10\x00\x01D\x0f`\xc8\xb4\x08\x01Dg\x0f@\
+ \x00\x02\x88\x1e\xc0\x90i\x11\x02\xa9\x89\xde\xafo7\
+9\xa9\xb2\x03\xc4!\x90\x1a\x81\x0f?\xdc!\xe3'>\
+\x92\xdazy^(5\xd1o\x9bDw\x0d\xbf\xfe\xca\x0c9\xbc\xed!\xb9\xec\
+\x1d\xd1\xf5cAt=\xabBE\xfa\x12\xbd{\xb7\xf6\
+\xf2\xf4\x9a)\xb9\xec\x1d\xd1\xf5cAt=\xabBE\
+\xfa\x12\xdd5}\xc3\xf5\x03e\xcc\xe8A\xb9\xeb\x1f\xd1\
+\xf5#At=\xabBE\xfa\x14\xdd5\xfe\xf8\xcaI\
+rr\x8fN\xb9b\x80\xe8\xfaq \xba\x9eU\xa1\x22\
+}\x8b~\xe8\xa1\xdf\x90_n\xac\xc9\x15\x03D\xd7\x8f\
+\x03\xd1\xf5\xac\x0a\x15\xe9[t\xd7\xfcE\x17\xf6\x96\xfb\
+\xab\x87\xe4\x86\x03\xa2\xebG\x81\xe8zV\x85\x8aLB\
+t\x07\xe0\xc1\x9aa2p@\xcf\x5c\xb0@t\xfd\x18\
+\x10]\xcf\xaaP\x91I\x89\xbe\xef\xbe{\xc9o\xde~\
+8\x17\xef\x9aCt\xfd\x96Dt=\xabBE&%\
+\xba\x83p\xfai\xc7\xc9\x92\x857g\xce\x03\xd1\xf5#\
+@t=\xabBE&)\xba\x031i\xc2\xa52t\
+\xc89\x992At=~D\xd7\xb3*Td\xd2\xa2\
+;\x18/\xad\xbbG:v843.\x88\xaeG\x8f\
+\xe8zV\x85\x8aLC\xf4\xceG\xb5\x96uk\xa7e\
+\xc6\x05\xd1\xf5\xe8\x11]\xcf\xaaP\x91i\x88\xee\x80\x0c\
+\xa9:[&O\xba,\x136\x88\xae\xc7\x8e\xe8zV\
+\x85\x8aLKt\x07e\xd1c7I\x9f3\x8eO\x9d\
+\x0f\xa2\xeb\x91#\xba\x9eU\xa1\x22\xd3\x14\xfd\xc0\x03\xbf\
+&\x9bk_rK\xfbBt=qD\xd7\xb3*T\
+d\x9a\xa2;0\x03\xce;Y~\xf4\xe0u\xa92B\
+t=nD\xd7\xb3*Td\xda\xa2;8\xf7N\xaf\
+\x92K.>=5N\x88\xaeG\x8d\xe8zV\x85\x8a\
+\xccBt\x07h\xeb\x96\x85\xd2\xbc\xf9>\xa9\xb0Bt\
+=fD\xd7\xb3*TdV\xa2\x9fT\xd9Q\xd6\xac\
+\xbe5\x15V\x88\xae\xc7\x8c\xe8zV\x85\x8a\xccJt\
+\x07\xe9\xc6Q\x83d\xd4\xc8\x81\x89\xf3Bt=bD\
+\xd7\xb3*T\xa4Ut\xf7Y\xe7\x1f\x7f\xfc\xd7\xd8\xbd\
+\xbe\xf2\xd2\xbd\xd2\xee\xc8\x16\xb1\xeb4U\x00\xd1\xf5x\
+\x11]\xcf\xaaP\x91V\xd1[\xb48P6o\xde\x12\
+\xbb\xd7\xc3\xda\x1c,\x1b_\x9b\x19\xbb\x0e\xa2\xfbA\x88\
+\xe8~8\xe6\xae\x8aU\xf4\x96-\x0f\x94)\x93\x06\xcb\
+\xbf\x0d\x1c\x1f\xbb\xa7\xcb.=C\xee\xb9\xfb\xca\xd8u\
+\x1a+\xc0\x89\xaeG\x8b\xe8zV\x85\x8a\xb4\x8a\xde\xea\
+\xd0\x83>\xfd\x96\x96s\x07\x8c\x93\xe5+^\x8c\xdd\xf3\
+\x92\x85ck\x1fk\xed\x12\xbbNC\x05\x10]\x8f\x15\
+\xd1\xf5\xac\x0a\x15i\x15\xbdu\xabo\xca\xa6\x0d\xb3>\
+\xedu\x9f\xfd\xe2\x7fy\xc3\x97\xf7\xdbW\xfe\xf4\xde\x82\
+D\xd8!\xba\x1e+\xa2\xebY\x15*\xd2*z\x9b\xd6\
+\xdf\x947^\xffLt\xf7M\xa5]\xbbW\xc5\xee\xfb\
+\xac3\xbb\xca\x82ycb\xd7\xa9_\x00\xd1\xf5H\x11\
+]\xcf\xaaP\x91V\xd1\xdb\x1ev\x88lxu\xc6\xdf\
+{\xbdv\xf8\x0c\xa9\xbe\xef'\xb1{O\xe2\xeb\x9d\x10\
+]?\x16D\xd7\xb3*T\xa4Ut\xf7\xf5K\xeek\
+\x98\xea^\x07}\xeb|\xd9\xb6\xed\xa3\xd8\xfd\xff\xf9\xfd\
+\xc5\xb2\xcf>{\xc5\xae\xf3y\x01D\xd7\xa3Dt=\
+\xabBEZE?\xe2\xf0o\xc9k/\xdf\xbf[\xaf\
+\xdb\xfe\xfc\x179\xe8\x90\x01\xb1\xfb?\xee\xd8\xb6\xf2\xc2\
+\xb3Sc\xd7A\xf4\xe8\x08\x11=:\xb3BdXE\
+worqov\xa9\x7f\xdd=}\xb1\x8c\x189;\
+v\xef\xc3\xae9W\xc6\x8f\xbb8v\x1dW\x80\x13]\
+\x8f\x11\xd1\xf5\xac\x0a\x15i\x15\xbd}\xbb\x16\xf2\xf2\xcf\
+\xf7\x14\xdd5\x7f\xec\xf1\x97\xcb\xa6_l\x8e\xcd\xc1\xfd\
+\x0d\xc0\xfd- \xee\x85\xe8z\x82\x88\xaegU\xa8H\
+\xab\xe8\x1d\xda\xb7\x94\xf5/V7\xda\xab\x8f\x97\xdc\x0e\
+>\xf8\x9f\xe4\xed7\x7f\x14\x9b'\xa2\xeb\x11\x22\xba\x9e\
+U\xa1\x22\xad\xa2\xbbOuu\x9f\xee\xda\xd8\xb5x\xe9\
+Z\x19\xf8\xdd\x89\xb1Y\x5c0\xf0\xdb\xf2\xc0\xackc\
+\xd5At=>D\xd7\xb3*T\xa4U\xf4\xa3:\xb5\
+\x92\x17\x7f6\xbd\xc9^\xcf>w\xac\xacZ\xfdRl\
+\x1e\xcb\x96\xdc\x22\xdf\xe9u\x8c\xb9\xce\xec\x9aUr\xe5\
+\x90\xc6\xff\xa7\xd4T\xe1\xed\x1f,\x15\xf7\x00O(\x17\
+\xa2\x97\xe9\xa4\xad\xa2k?\xc2\xb9\xf9\xfe}\xe5\x93O\
+v\xc6\xa2\xd7\xacY\x85\xec\xd8\xbe\xdc\x5cc\xd6\xec\x95\
+Ruu\xe3\xbff \xfa\x17\x04\x10\xdd\xbc\xcd\xf2\x9d\
+h\x15\xfd\xe8\xce\xad\xe5\xbf^(\xfdY\xed/\xbf\xf2\
++9\xb1\xc7\xd5\xb1!\xf4<\xa5\xb3\xacZ>\xc1T\
+g\xe6\xac\x15r\xd5\xd0\x86\xffpX\xaa 'z)\
+B\xfe\xfe{\xc5\xde\xcd\xfb\xec\xf2W\xae\xf1J\xb7M\
+\x1e,UW\xf4Oc\xa9\xdc\xaca\x15\xfd\xd8c\x0e\
+\x93\xb5\xcf\xdd\xa5\xeacH\xadd3je\x8b{U\
+O\xab\x92K/\x89\xfeYs3f\xae\x90!\xd7 \
+\xba\x86?'\xba\x86R\x01c\xac\xa2G}SK\x9b\
+#.\x92w\xdf\xfdSlB\x1fm[*_\xfaR\
+\xb4\xdf\x99\xef\x9b\xb1L\x86^\xbb\xfb\x9b{\xb47\xc2\
+\x89\xae%\x15?\x8e\x13=>\xc3F+\x98E?\xae\
+\xf6\xddk\xcf\xe8\xdf\xbd\xb6u\xebvqk\xc5\xbd\x8e\
+<\xe2\x9f\xe5\xd5\xf5\xf7E*s\xef\xfd\xcb\xe4\x9aa\
+\x88\xae\x81\xc6\x89\xae\xa1T\xc0\x18\xab\xe8]\xba\x1c.\
+\xcf\xff\xf4\xceH\x1d\xdf1u\x81\x8c\xba\xf1\xc1H9\
+\x0d\x05\x8f\x18>@n\xbe\xe9\xfb\xea:\xeea\x1b\xf7\
+\xd0\x8d\xe5\xe2D\xb7P\xb3\xe5p\xa2\xdb\xb8\xa9\xb2\xac\
+\xa2w\xedz\x84<\xfb\xd4\x1d\xaa5\xea\x06U\x9e2\
+T\xd6\xaf\x7f+r^\xfd\x84M\xb5\x8f\xc8\xb6\xae}\
+TVsM\xaf^\x22\xc3G|\xf6Hm\xd4\x0b\xd1\
+\xa3\x12\xb3\xc7#\xba\x9d]\xc9L\xab\xe8't=R\
+\x9ey\xea\xf6\x92\xf5\x1b\x0a\xf0\xf1\xae\xb9\xaf}u?\
+\xf9\xc3\xbb\xf3T\xebO\xbbg\x89\x5cw=\xa2k`\
+\xf1\xa3\xbb\x86R\x01c\xac\xa2w;\xa1\x9d\xfc\xf4\xc9\
+\xdbL\x1d/X\xf4\xbc\x0c\xfa~\xfc\xcft?\x7f@\
+O\xf9\xcf\x9aa%\xef!\xce\x836\x9c\xe8%\xf1z\
+\x0b\xe0D\xf7\x86r\xcfBV\xd1O\xec\xde^\x9ez\
+b\x8a\xf9\xce\xce;\xff\x16Y\xb6|\x9d9\xff\xf3D\
+\xf7\xda\xba{\x8d\xbd\xa9k\xea\xdd\x8bd\xe4\xa8\x1a\xd3\
+Z\x88n\xc2fJBt\x136]\x92U\xf4\xca\x13\
+;\xc8\x93\x8fO\xd6-\xd2H\xd4\xd7\x0f:O\xb6\x7f\
+\xb4#V\x0d\x97\xfc\xbf\x7fi\xfa5\xfa;\xefZ(\
+7\x8c~\xc0\xb4\x0e\xa2\x9b\xb0\x99\x92\x10\xdd\x84M\x97\
+d\x15\xdd\xc7W*\xfd\xfc\xa57\xa5G\xcfkt7\
+\xdaDT\xf7n\xed\xe5\xe95\x8d\xfft\x11\xe7\xaf\xfd\
+\x88\x1e{<\xea\x02\x88\xaeF\x15=\xd0*z\x8f\x93\
+:\xca\x13\xab\xe2\xff\x9e]5\xa4Zf\xd5\xac\x8c~\
+\xe3\xf52\xee\xaf\x1e\x22\x17]\xd8\xbb\xc1:\xb7\xdf\xb9\
+@F\x8f\xb1\xbd\xac\x87\xe8\xb1G\xa3.\x80\xe8jT\
+\xd1\x03\xad\xa2\x9fr\xf2Q\xb2zE\xfc\xc7P\xdd\x1d\
+w:\xfa\xdf\xe5\xad_\xbd\x1b\xfd\xe6\xebe\xec\xd8\xbe\
+L\x9a5k\xb6G\x9d)\xb7\xcf\x971cm\xcf\xb5\
+#z\xec\xb1\xa8\x0b \xba\x1aU\xf4@\xab\xe8q\x1e\
+2\xa9\x7f\x97[\xb6l\x93\x16\xad\x07E\xbf\xf9z\x19\
+\xeek\xa2\xde\xdc\xb4\xe7\xef\xe2\x93o\x9b'7\xdd\xfc\
+cS}D7a3%!\xba\x09\x9b.\xc9*\xfa\
+\xb7{v\x96\x95\xcblO\x935tgqN\xdd\xba\
+\xf5n\xb8~\xa0\x8c\x19\xbd\xfb\xff4n\x9d2O\xc6\
+\x8eCt\xcd\x8e\xe0ut\x0d\xa5\x02\xc6XE\xefu\
+\xea1\xb2|\xe9-^;>\xad\xcfHy\xf6\xb9\x0d\
+\xb1k\xfe\xf7\x1b5\xd2\xb2\xe57\xfe^g\xe2\xads\
+e\xdc\xf8\x87Mu9\xd1M\xd8LI\x9c\xe8&l\
+\xba$\xab\xe8\xee\x13_\xdc'\xbf\xf8\xbe|\xbck\xce\
+}\x22\x8c\x13\xf4\xf3k\xc2\xa4\xb9r\xcb\x04D\xd7\xcc\
+\x8a\x13]C\xa9\x801V\xd1{\x7f\xe7X\xf9\xc9\xe2\
+q\xde;~t\xfe3r\xd1%\xb6w\xdc\xd5\xbd\x99\
+\xef^p\xaa\xd4\xcc\xfc\xec\xa5\xbb\xf1\x13\xe7\xd4\xfe{\
+\xc4t\xaf\x9c\xe8&l\xa6$Nt\x136]\x92U\
+\xf4\xd3z\x1f'K\x17\xdd\xac[$b\xd4\x85\xb5\xa2\
+\xcf\xab\x15>\xee\xf5\xc4\xaaI\xd2\xe3\xa4N\xb5\xa7\xf9\
+#2a\xd2\x1cS9D7a3%!\xba\x09\x9b\
+.\xc9*\xfa\x19\xa7w\x91\xc5\x0b\xc6\xea\x161D\xb5\
+l\xf3=y\xef\xbd\x0f\x0c\x99\xbb\xa7\xb8w\xcd\xb9\xdf\
+\xcf\xdd\xef\xe9\x96\x0b\xd1-\xd4l9\x88n\xe3\xa6\xca\
+\xb2\x8a\xde\xe7\x8c\xe3e\xd1c7\xa9\xd6\xb0\x04\xad[\
+\xf7K9\xa5W\xe9\x07VJ\xd5v\xef\xe0\xab<\xb1\
+\xbdL\x9a\xfch\xa9\xd0\x06\xff;\xa2\x9b\xb0\x99\x92\x10\
+\xdd\x84M\x97d\x15\xfd\xcc>]e\xe1|\xff_q\
+\x5c\xf7\xae\xaf\xbf\xa1F\xee\x9a\xb6H\xd7H\x13Q\xee\
+^W\xac|\xd1T\x07\xd1M\xd8LI\x88n\xc2\xa6\
+K\xb2\x8a\xde\xf7\xac\x13\xe4\xb1Go\xd4-\x12#\xaa\
+[\xe5U\xf2\xeako\xc7\xa8\x10/\x15\xd1\xe3\xf1\x8b\
+\x92\x8d\xe8QhE\x8c\xb5\x8a\xde\xafo7\x99?w\
+t\xc4\xd5\xa2\x87\xff\xfe\xf7[\xa5U[\xfd\xc7FE\
+_\xa1\xe9\x0cD\xf7M\xb4\xf1z\x88\x9e k\xab\xe8\
+\xff\xd2\xaf\x9b\xcc\x9b\x93\xbc\xe8\xaeu\xf7\xe3\xbb\xfb1\
+>\x8b\x0b\xd1\xd3\xa3\x8e\xe8\x09\xb2\xb6\x8a\xde\xbf_w\
+yt\xce\xa8\x04\xefl\xf7\xd2\xbe\xbe\xde)\xea\x0d#\
+zTb\xf6xD\xb7\xb3+\x99i\x15\xfd\x9c\xfe\x95\
+2\xe7\xe1\x91%\xeb\xfb\x0c\xf8\xc7\xaf\x9f#;v\xfc\
+\x9f\xcf\x92%k!zID\xde\x02\x10\xdd\x1b\xca=\
+\x0b\x15I\xf4y\x8f=+\x17^l\xff\xf8*\x0bF\
+D\xb7P\xb3\xe5 \xba\x8d\x9b*\xabH\xa2\xbb\x86.\
+\xaf\x9a.\x0f<\xb8Z\xd5\x9b\x8f D\xf7AQW\
+\x03\xd1u\x9cLQE\x13\xdd5\xd9\xae\xe3e\xf2\xce\
+\xaf\xff`\xea7j\x12\xa2G%f\x8fGt;\xbb\
+\x92\x99E\x14\xfd\x85\xb5oH\xaf\xd3F\x94\xec\xcdG\
+\x00\xa2\xfb\xa0\xa8\xab\x81\xe8:N\xa6\xa8\x22\x8a\xee\x1a\
+\x1dW\xfb\xd8\xe9\xc4\xda\xc7O\x93\xbe\x10=i\xc2_\
+\xd4G\xf4\x04Y\x17Ut\x87\xe4\xd4\xde\xd7\xc9\xda\x9f\
+mJ\x90\x8e|\xfa\x5c\xbb{\xbe=\x94\x8b\xe7\xd1\xcb\
+t\xd2E\x16\xfd\xb7\xbf\xfd\xa3\xb4mwq\xa2\x93A\
+\xf4D\xf1\xeeV\x9c\x13=A\xd6E\x16\xdda\x995\
+{\xa5T]]\x9d\x18!DO\x0c\xed\x1e\x85\x11=\
+A\xd6E\x17\xdd\xa1q\xdf\xe3\xe6\xbe\xcf-\x89\x0b\xd1\
+\x93\xa0\xdapMDO\x90u9\x88\xee\xf0\x1c\xd2\xf2\
+\x02y\xff\xfd\x0f\xbd\x93Bt\xefH\x1b-\x88\xe8\x09\
+\xb2.\x17\xd1\xdd\x176\xba/n\xf4}!\xbao\xa2\
+\x8d\xd7C\xf4\x04Y\x97\x8b\xe8\x0e\xd1\xb0\xebf\xca=\
+\xf7~\xf1\xe9\xaf>\xb0!\xba\x0f\x8a\xba\x1a\x88\xae\xe3\
+d\x8a*'\xd1\x1d\x80.'\x5c)\x1b\xdf\xf8\xb5\x89\
+ECI\x88\xee\x0de\xc9B\x88^\x12\x91=\xa0\xdc\
+D_\xff\xf2[Ry\xf2P;\x90z\x99\x88\xee\x0d\
+e\xc9B\x88^\x12\x91=\xa0\xdcDw$\xe2|{\
+j}\x92\x88n\xdf[Q3\x11=*\xb1\x08\xf1\xe5\
+(\xbak\xbfo\xff\x1be\xcd\x93\xafD \xd1p(\
+\xa2\xc7F\xa8.\x80\xe8jT\xd1\x03\xcbU\xf4\xdf\xfd\
+\xee}9\xb2\xe3\xa5\xf2\xf1\xc7\x7f\x8d\x0e\xa5N\x06\xa2\
+\xc7\xc2\x17)\x19\xd1#\xe1\x8a\x16\x5c\xae\xa2;\x0as\
+\xe6>-\x97\x0c\xbe#\x1a\x10~G\x97\xe9\xd5~_\
+\xb9\xd0\x0e\x00\xd1\xb5\xa4\x0cq\xe5,\xba\xc3\xf1\x83\x1f\
+\xde%?~h\x8d\x81\xccg)\x9c\xe8ft\x91\x13\
+\x11=22}B\xb9\x8b\xfe\xc9';\xa5]\xa7\xcb\
+d\xf3\xe6-z(\xfc\xe8nb\x157\x09\xd1\xe3\x12\
+l\x22\x7f\xd7\xae]\xe6\xea\x15\x15\x15\xe6\xdc4\x13C\
+\xe8\xd1\x17O\x1eS\xf5E\x92:\x10\xc81\x01D\xcf\
+\xf1p\xb85\x08\xf8\x22\x80\xe8\xbeHR\x07\x029&\
+\x80\xe89\x1e\x0e\xb7\x06\x01_\x04\x10\xdd\x17I\xea@\
+ \xc7\x04\x10=\xc7\xc3\xe1\xd6 \xe0\x8b\x00\xa2\xfb\x22\
+I\x1d\x08\xe4\x98\x00\xa2\xe7x8\xdc\x1a\x04|\x11@\
+t_$\xa9\x03\x81\x1c\x13@\xf4\x1c\x0f\x87[\x83\x80\
+/\x02\x88\xee\x8b$u \x90c\x02\x88\x9e\xe3\xe1p\
+k\x10\xf0E\x00\xd1}\x91\xa4\x0e\x04rL\x00\xd1s\
+<\x1cn\x0d\x02\xbe\x08 \xba/\x92\xd4\x81@\x8e\x09\
+ z\x8e\x87\xc3\xadA\xc0\x17\x01D\xf7E\x92:\x10\
+\xc81\x81 D\xff\xea\xfe\xcd\xe5\xf1U\xb7\xcaQ\x9d\
+Z\xe5x\x14\xdc\x1a\x04\x92#\x10\x84\xe8\x0e\x1f\xb2'\
+\xb7\x89\xa8\x9c\x7f\x02\xc1\x88\x8e\xec\xf9\xdf\x8c\xdcar\
+\x04\x82\x12\x1d\xd9\x93\xdbHT\xce7\x81\xe0DG\xf6\
+|oH\xee.\x19\x02A\x8a\x8e\xec\xc9l&\xaa\xe6\
+\x97@\xb0\xa2#{~7%w\xe6\x9f@\xd0\xa2#\
+\xbb\xff\x0dE\xc5|\x12\x08^td\xcf\xe7\xc6\xe4\xae\
+\xfc\x12@\xf4\xbf\xf1\xe4uv\xbf\x1b\x8bj\xf9\x22\x80\
+\xe8u\xe6\x81\xec\xf9\xda\x9c\xdc\x8d?\x02\x88^\x8f%\
+\xb2\xfb\xdb\x5cT\xca\x0f\x01Do`\x16\xc8\x9e\x9f\x0d\
+\xca\x9d\xf8!\x80\xe8\x8dpDv?\x1b\x8c*\xf9 \
+\x80\xe8M\xcc\x01\xd9\xf3\xb1I\xb9\x8b\xf8\x04\x10\xbd\x04\
+Cd\x8f\xbf\xc9\xa8\x90=\x01DW\xcc\x00\xd9\x15\x90\
+\x08\xc95\x01DW\x8e\x07\xd9\x95\xa0\x08\xcb%\x01D\
+\x8f0\x16d\x8f\x00\x8b\xd0\x5c\x11@\xf4\x88\xe3@\xf6\
+\x88\xc0\x08\xcf\x05\x01D7\x8c\x01\xd9\x0d\xd0H\xc9\x94\
+\x00\xa2\x1b\xf1#\xbb\x11\x1ci\x99\x10@\xf4\x18\xd8\x91\
+=\x06\
+\
+\
+\x00\x00\x04\xed\
+<\
+svg xmlns=\x22http:\
+//www.w3.org/200\
+0/svg\x22 viewBox=\x22\
+0 0 512 512\x22 sty\
+le=\x22enable-backg\
+round:new 0 0 51\
+2 512\x22 xml:space\
+=\x22preserve\x22><\
+path style=\x22fill\
+:#d80027\x22 d=\x22M20\
+0.348 196.634 0 \
+85.33v31.474l143\
+.693 79.83z\x22/>\
+\x00\x00\x00\xf4\
+<\
+svg xmlns=\x22http:\
+//www.w3.org/200\
+0/svg\x22 viewBox=\x22\
+0 0 512 512\x22 sty\
+le=\x22enable-backg\
+round:new 0 0 51\
+2 512\x22 xml:space\
+=\x22preserve\x22>\
+\x00\x00\x00\xf1\
+<\
+svg xmlns=\x22http:\
+//www.w3.org/200\
+0/svg\x22 viewBox=\x22\
+0 0 512 512\x22 sty\
+le=\x22enable-backg\
+round:new 0 0 51\
+2 512\x22 xml:space\
+=\x22preserve\x22>\
+\
+\x00\x00\x03\x8a\
+<\
+svg xmlns=\x22http:\
+//www.w3.org/200\
+0/svg\x22 width=\x2240\
+\x22 height=\x2240\x22 vi\
+ewBox=\x220 0 40 40\
+\x22>\x0d\x0a\x0d\x0a\x0d\x0a\x0d\x0a\x0d\x0a\x0d\
+\x0a\x0d\x0a\
+\x00\x00\x1e\xeb\
+<\
+\xb8d\x18\xca\xef\x9c\x95\xcd!\x1c\xbf`\xa1\xbd\xdd\xa7\
+\x00\x00\x00\x05en_USB\x00\x00\x030\x00\x04\
+\xa8\x8b\x00\x00\x16\xd4\x00\x05 D\x00\x00\x03\xca\x00\x05\
+0\xa4\x00\x00\x01 \x00\x052\xde\x00\x00\x0au\x00\x05\
+7\xfe\x00\x00\x06\x1e\x00\x05g\xd5\x00\x00\x11\x90\x00 \
+\x7fy\x00\x00\x06~\x00 \xfdw\x00\x00\x11\xb6\x00G\
+\x96\xc4\x00\x00\x12\xb3\x00Hw9\x00\x00\x02p\x00J\
+c\xf3\x00\x00\x0d\x90\x00R\xfd\xf4\x00\x00\x17\xd2\x00S\
+]\xfe\x00\x00\x01G\x00S\x84\xd1\x00\x00\x0e2\x00V\
+\x8a\xc2\x00\x00\x04L\x00V\xae\xc2\x00\x00\x0f\x1c\x00X\
+\xc9\xc4\x00\x00\x055\x00\xaa\xb4\x93\x00\x00\x01\xd1\x00\xbf\
+\xf1\xf4\x00\x00\x00~\x01'\xda.\x00\x00\x0bC\x01L\
+\x05#\x00\x00\x15\xce\x01\xa5\xcc{\x00\x00\x09\x00\x01\xac\
+\x0a~\x00\x00\x0cI\x02\xf9\x88\xc9\x00\x00\x05\xea\x03{\
+\x0a~\x00\x00\x09\x9b\x03\xdbsg\x00\x00\x03\xef\x03\xdb\
+sg\x00\x00\x06I\x04r\x80\xd4\x00\x00\x03\x00\x04\x87\
+\xd7.\x00\x00\x0c\x8c\x04\x98I\xbc\x00\x00\x00Q\x04\xac\
+,\xa5\x00\x00\x00\xc0\x04\xb6\xc0\xfe\x00\x00\x0d\xba\x04\xc0\
+RN\x00\x00\x09I\x04\xd9p\xbe\x00\x00\x1b\x00\x05\x22\
+?\xe9\x00\x00\x18\xb3\x058O\xb1\x00\x00\x0e\x5c\x058\
+O\xd1\x00\x00\x0e\x89\x05\x88U\x85\x00\x00\x0fF\x05\x88\
+\xb0G\x00\x00\x01\xa4\x05\x88\xb0G\x00\x00\x11\xfd\x05\x90\
+_\xc2\x00\x00\x07\xe8\x05\x9b\x88\x98\x00\x00\x05]\x05\xaa\
+\x8b\xc3\x00\x00\x12X\x05\xb5\x1b\xbe\x00\x00\x0a\xd9\x05\xc6\
+\xa8\xa5\x00\x00\x02C\x06\x06\xf6t\x00\x00\x04\x1e\x06!\
+\xd8#\x00\x00\x0d\xe7\x06L\x9f4\x00\x00\x0e\xec\x06N\
+`\x17\x00\x00\x1b-\x06\xc2\xecl\x00\x00\x08w\x07\x0a\
+^\x03\x00\x00\x17\x06\x07(T\xe4\x00\x00\x04t\x07\x9a\
+\x80\xd3\x00\x00\x16'\x08N\xb2\xf5\x00\x00\x17\x94\x08^\
+\x80\x03\x00\x00\x07\x84\x08hI\x95\x00\x00\x10\x7f\x08~\
+2\x13\x00\x00\x02\xc9\x08\x80\x13^\x00\x00\x10\xea\x08\x9e\
+=\xd9\x00\x00\x06\xb8\x08\xa81s\x00\x00\x02\x98\x08\xaa\
+\xe3\xe4\x00\x00\x01q\x08\xb1D~\x00\x00\x0a\x08\x08\xb2\
+(\x07\x00\x00\x04\xa2\x08\xb7\xb0\x17\x00\x00\x04\xd9\x08\xc2\
+\x8a\xe4\x00\x00\x05\x07\x09Q\xe9\xd3\x00\x00\x14k\x09\x9a\
+\xc0\x05\x00\x00\x00\x00\x09\xbac\xa3\x00\x00\x0fs\x09\xc3\
+\x84\x81\x00\x00\x0f\xa3\x09\xca\x9f.\x00\x00\x10\xb6\x09\xfe\
+\x06\xae\x00\x00\x02\x13\x09\xfe\x06\xae\x00\x00\x05\x88\x09\xfe\
+\x06\xae\x00\x00\x12)\x0a+w\x1e\x00\x00\x13\xd9\x0aU\
+\x903\x00\x00\x06\xfb\x0a{z\x85\x00\x00\x19}\x0a\xae\
+\xbe\xae\x00\x00\x18\x07\x0a\xbc\xb8\xf4\x00\x00\x0f\xd3\x0a\xbc\
+\xb8\xf4\x00\x00\x11$\x0a\xdd\x0b>\x00\x00\x14\xfd\x0bV\
+\x5c\x94\x00\x00\x13A\x0b\x96v\xf4\x00\x00\x034\x0c\x07\
+\x9cT\x00\x00\x0dM\x0c\x1c\xf5$\x00\x00\x03\x90\x0c+\
+\x8c4\x00\x00\x1b]\x0cN0\xd8\x00\x00\x10K\x0cf\
+\xa1u\x00\x00\x00\xed\x0c\xa8h\x87\x00\x00\x1a\xa4\x0c\xbb\
+\x01s\x00\x00\x19?\x0c\xdc\x9d-\x00\x00\x17D\x0c\xe4\
+\xbb\xc9\x00\x00\x08\xae\x0d\x03\xac\xd3\x00\x00\x03b\x0dc\
+\xc3\x93\x00\x00\x12\x84\x0do\x06\x14\x00\x00\x07G\x0d\xa5\
+\x0f\x13\x00\x00\x10\x09\x0e\x14\xdds\x00\x00\x08.\x0e`\
+o\xf3\x00\x00\x19\xd0\x0e\x87\xa8\x03\x00\x00\x11^\x0f\x0a\
+\xb6\xd9\x00\x00\x05\xb6\x0f2\x8b\xb2\x00\x00\x18Z\x0fC\
+7A\x00\x00\x0e\xb6\x0f\xaa3\xb5\x00\x00\x12\xe8i\x00\
+\x00\x1b\x90\x03\x00\x00\x00$\x00A\x00u\x00t\x00h\
+\x00o\x00r\x00i\x00z\x00a\x00t\x00i\x00o\
+\x00n\x00 \x00c\x00o\x00d\x00e\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x12Authorizat\
+ion code\x07\x00\x00\x00\x06Dia\
+log\x01\x03\x00\x00\x00\x0c\x00C\x00a\x00n\x00\
+c\x00e\x00l\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06C\
+ancel\x07\x00\x00\x00\x06Dialog\
+\x01\x03\x00\x00\x00\x1a\x00C\x00h\x00a\x00p\x00t\
+\x00e\x00r\x00s\x00 \x00r\x00e\x00a\x00d\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0dChapte\
+rs read\x07\x00\x00\x00\x06Dial\
+og\x01\x03\x00\x00\x00\x0c\x00D\x00e\x00l\x00e\
+\x00t\x00e\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06De\
+lete\x07\x00\x00\x00\x06Dialog\x01\
+\x03\x00\x00\x00\x10\x00G\x00e\x00t\x00 \x00c\x00\
+o\x00d\x00e\x08\x00\x00\x00\x00\x06\x00\x00\x00\x08G\
+et code\x07\x00\x00\x00\x06Dial\
+og\x01\x03\x00\x00\x00\x08\x00L\x00i\x00s\x00t\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04List\x07\x00\
+\x00\x00\x06Dialog\x01\x03\x00\x00\x00\x0a\x00\
+L\x00o\x00g\x00i\x00n\x08\x00\x00\x00\x00\x06\x00\
+\x00\x00\x05Login\x07\x00\x00\x00\x06Dia\
+log\x01\x03\x00\x00\x00\x10\x00P\x00a\x00s\x00\
+s\x00w\x00o\x00r\x00d\x08\x00\x00\x00\x00\x06\x00\
+\x00\x00\x08Password\x07\x00\x00\x00\x06\
+Dialog\x01\x03\x00\x00\x00\x0c\x00R\x00a\
+\x00t\x00i\x00n\x00g\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x06Rating\x07\x00\x00\x00\x06Dia\
+log\x01\x03\x00\x00\x00\x1a\x00S\x00h\x00o\x00\
+w\x00 \x00s\x00p\x00o\x00i\x00l\x00e\x00\
+r\x00s\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0dSho\
+w spoilers\x07\x00\x00\x00\x06D\
+ialog\x01\x03\x00\x00\x00\x0e\x00S\x00i\x00\
+g\x00n\x00 \x00i\x00n\x08\x00\x00\x00\x00\x06\x00\
+\x00\x00\x07Sign in\x07\x00\x00\x00\x06D\
+ialog\x01\x03\x00\x00\x00\x0c\x00U\x00p\x00\
+d\x00a\x00t\x00e\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x06Update\x07\x00\x00\x00\x06Dial\
+og\x01\x03\x00\x00\x00\x0a\x00A\x00p\x00p\x00l\
+\x00y\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05Appl\
+y\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\x10\
+\x00C\x00a\x00t\x00a\x00l\x00o\x00g\x00s\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x08Catalo\
+gs\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\
+\x14\x00C\x00h\x00a\x00r\x00a\x00c\x00t\x00\
+e\x00r\x00s\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0aC\
+haracters\x07\x00\x00\x00\x04Fo\
+rm\x01\x03\x00\x00\x00\x12\x00C\x00o\x00m\x00p\
+\x00l\x00e\x00t\x00e\x00d\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x09Completed\x07\x00\x00\
+\x00\x04Form\x01\x03\x00\x00\x00\x0e\x00D\x00r\
+\x00o\x00p\x00p\x00e\x00d\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x07Dropped\x07\x00\x00\x00\x04\
+Form\x01\x03\x00\x00\x00\x0e\x00F\x00i\x00l\
+\x00t\x00e\x00r\x00s\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x07Filters\x07\x00\x00\x00\x04Fo\
+rm\x01\x03\x00\x00\x00\x16\x00G\x00e\x00n\x00r\
+\x00e\x00s\x00 \x00l\x00i\x00s\x00t\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x0bGenres l\
+ist\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\
+\x00\x08\x00K\x00i\x00n\x00d\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x04Kind\x07\x00\x00\x00\x04For\
+m\x01\x03\x00\x00\x00\x0c\x00N\x00l\x00i\x00g\x00\
+h\x00t\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0aMai\
+nWindow\x07\x00\x00\x00\x04Form\
+\x01\x03\x00\x00\x00\x0e\x00O\x00n\x00 \x00h\x00o\
+\x00l\x00d\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07On\
+ hold\x07\x00\x00\x00\x04Form\x01\x03\
+\x00\x00\x00\x0a\x00O\x00r\x00d\x00e\x00r\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x05Order\x07\x00\x00\
+\x00\x04Form\x01\x03\x00\x00\x00\x0e\x00P\x00l\
+\x00a\x00n\x00n\x00e\x00d\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x07Planned\x07\x00\x00\x00\x04\
+Form\x01\x03\x00\x00\x00\x14\x00R\x00e\x00-\
+\x00r\x00e\x00a\x00d\x00i\x00n\x00g\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x0aRe-readi\
+ng\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\
+\x0e\x00R\x00e\x00a\x00d\x00i\x00n\x00g\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x07Reading\
+\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\x0e\x00\
+R\x00e\x00l\x00a\x00t\x00e\x00d\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00\x07Related\x07\x00\
+\x00\x00\x04Form\x01\x03\x00\x00\x00\x0a\x00R\x00\
+e\x00s\x00e\x00t\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x05Reset\x07\x00\x00\x00\x04Form\x01\
+\x03\x00\x00\x00\x0c\x00S\x00e\x00a\x00r\x00c\x00\
+h\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06Searc\
+h\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\x0e\
+\x00S\x00i\x00g\x00n\x00 \x00i\x00n\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x07Sign in\x07\
+\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\x0e\x00H\
+\x00i\x00s\x00t\x00o\x00r\x00y\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x07History\x07\x00\x00\
+\x00\x0aMainWindow\x01\x03\x00\x00\
+\x00\x0e\x00L\x00i\x00b\x00r\x00a\x00r\x00y\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Librar\
+y\x07\x00\x00\x00\x0aMainWindow\
+\x01\x03\x00\x00\x00\x08\x00M\x00a\x00i\x00n\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x04Main\x07\x00\x00\x00\
+\x0aMainWindow\x01\x03\x00\x00\x00\
+\x0c\x00N\x00l\x00i\x00g\x00h\x00t\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00\x0aMainWindo\
+w\x07\x00\x00\x00\x0aMainWindow\
+\x01\x03\x00\x00\x00\x12\x00S\x00h\x00i\x00k\x00i\
+\x00m\x00o\x00r\x00i\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x09Shikimori\x07\x00\x00\x00\x0a\
+MainWindow\x01\x03\x00\x00\x00\x1c\
+\x00A\x00d\x00d\x00 \x00t\x00o\x00 \x00L\
+\x00i\x00b\x00r\x00a\x00r\x00y\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x0eAdd to Lib\
+rary\x07\x00\x00\x00\x04Menu\x01\x03\x00\
+\x00\x00\x22\x00C\x00l\x00e\x00a\x00r\x00 \x00\
+l\x00o\x00c\x00a\x00l\x00 \x00f\x00i\x00\
+l\x00e\x00s\x08\x00\x00\x00\x00\x06\x00\x00\x00\x11C\
+lear local files\
+\x07\x00\x00\x00\x04Menu\x01\x03\x00\x00\x00\x18\x00\
+M\x00a\x00r\x00k\x00 \x00a\x00s\x00 \x00\
+r\x00e\x00a\x00d\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x0cMark as read\x07\x00\x00\
+\x00\x04Menu\x01\x03\x00\x00\x002\x00M\x00a\
+\x00r\x00k\x00 \x00a\x00s\x00 \x00r\x00e\
+\x00a\x00d\x00 \x00a\x00l\x00l\x00 \x00p\
+\x00r\x00e\x00v\x00i\x00o\x00u\x00s\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x19Mark as \
+read all previou\
+s\x07\x00\x00\x00\x04Menu\x01\x03\x00\x00\x00\x1e\
+\x00O\x00p\x00e\x00n\x00 \x00i\x00n\x00 \
+\x00b\x00r\x00o\x00w\x00s\x00e\x00r\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x0fOpen in \
+browser\x07\x00\x00\x00\x04Menu\
+\x01\x03\x00\x00\x00 \x00O\x00p\x00e\x00n\x00 \
+\x00l\x00o\x00c\x00a\x00l\x00 \x00f\x00i\
+\x00l\x00e\x00s\x08\x00\x00\x00\x00\x06\x00\x00\x00\x10\
+Open local files\
+\x07\x00\x00\x00\x04Menu\x01\x03\x00\x00\x00\x14\x00\
+R\x00e\x00m\x00o\x00v\x00e\x00 \x00a\x00\
+l\x00l\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0aRem\
+ove all\x07\x00\x00\x00\x04Menu\
+\x01\x03\x00\x00\x00&\x00R\x00e\x00m\x00o\x00v\
+\x00e\x00 \x00f\x00r\x00o\x00m\x00 \x00l\
+\x00i\x00b\x00r\x00a\x00r\x00y\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x13Remove fro\
+m library\x07\x00\x00\x00\x04Me\
+nu\x01\x03\x00\x00\x00 \x00R\x00e\x00m\x00o\
+\x00v\x00e\x00 \x00r\x00e\x00a\x00d\x00 \
+\x00m\x00a\x00r\x00k\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x10Remove read ma\
+rk\x07\x00\x00\x00\x04Menu\x01\x03\x00\x00\x00\
+$\x00C\x00h\x00e\x00c\x00k\x00 \x00f\x00\
+o\x00r\x00 \x00u\x00p\x00d\x00a\x00t\x00\
+e\x00s\x00.\x08\x00\x00\x00\x00\x06\x00\x00\x00\x12C\
+heck for updates\
+.\x07\x00\x00\x00\x07Message\x01\x03\x00\
+\x00\x006\x00E\x00r\x00r\x00o\x00r\x00 \x00\
+c\x00h\x00e\x00c\x00k\x00i\x00n\x00g\x00\
+ \x00f\x00o\x00r\x00 \x00u\x00p\x00d\x00\
+a\x00t\x00e\x00s\x00.\x08\x00\x00\x00\x00\x06\x00\
+\x00\x00\x1bError checkin\
+g for updates.\x07\x00\
+\x00\x00\x07Message\x01\x03\x00\x00\x006\
+\x00F\x00i\x00l\x00e\x00s\x00 \x00{\x00}\
+\x00 \x00h\x00a\x00v\x00e\x00 \x00b\x00e\
+\x00e\x00n\x00 \x00r\x00e\x00m\x00o\x00v\
+\x00e\x00d\x00.\x08\x00\x00\x00\x00\x06\x00\x00\x00\x1b\
+Files {} have be\
+en removed.\x07\x00\x00\x00\x07\
+Message\x01\x03\x00\x00\x000\x00M\x00\
+a\x00n\x00g\x00a\x00 \x00{\x00}\x00 \x00\
+h\x00a\x00s\x00 \x00b\x00e\x00e\x00n\x00\
+ \x00a\x00d\x00d\x00e\x00d\x00.\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00\x18Manga {} \
+has been added.\x07\
+\x00\x00\x00\x07Message\x01\x03\x00\x00\x00\
+4\x00M\x00a\x00n\x00g\x00a\x00 \x00{\x00\
+}\x00 \x00h\x00a\x00s\x00 \x00b\x00e\x00\
+e\x00n\x00 \x00d\x00e\x00l\x00e\x00t\x00\
+e\x00d\x00.\x08\x00\x00\x00\x00\x06\x00\x00\x00\x1aM\
+anga {} has been\
+ deleted.\x07\x00\x00\x00\x07Me\
+ssage\x01\x03\x00\x00\x00\x9c\x00N\x00e\x00\
+w\x00 \x00v\x00e\x00r\x00s\x00i\x00o\x00\
+n\x00 \x00{\x00r\x00e\x00s\x00u\x00l\x00\
+t\x00}\x00 \x00i\x00s\x00 \x00a\x00v\x00\
+a\x00i\x00l\x00a\x00b\x00l\x00e\x00!\x00\
+ \x00Y\x00o\x00u\x00 \x00a\x00r\x00e\x00\
+ \x00c\x00u\x00r\x00r\x00e\x00n\x00t\x00\
+l\x00y\x00 \x00o\x00n\x00 \x00v\x00e\x00\
+r\x00s\x00i\x00o\x00n\x00 \x00{\x00A\x00\
+P\x00P\x00_\x00V\x00E\x00R\x00S\x00I\x00\
+O\x00N\x00}\x00.\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+NNew version {re\
+sult} is availab\
+le! You are curr\
+ently on version\
+ {APP_VERSION}.\x07\
+\x00\x00\x00\x07Message\x01\x03\x00\x00\x00\
+\x1a\x00N\x00o\x00 \x00c\x00o\x00n\x00n\x00\
+e\x00c\x00t\x00i\x00o\x00n\x08\x00\x00\x00\x00\
+\x06\x00\x00\x00\x0dNo connecti\
+on\x07\x00\x00\x00\x07Message\x01\x03\
+\x00\x00\x00n\x00N\x00o\x00 \x00u\x00p\x00d\
+\x00a\x00t\x00e\x00s\x00 \x00a\x00v\x00a\
+\x00i\x00l\x00a\x00b\x00l\x00e\x00.\x00 \
+\x00Y\x00o\x00u\x00 \x00a\x00r\x00e\x00 \
+\x00u\x00s\x00i\x00n\x00g\x00 \x00t\x00h\
+\x00e\x00 \x00l\x00a\x00t\x00e\x00s\x00t\
+\x00 \x00v\x00e\x00r\x00s\x00i\x00o\x00n\
+\x00.\x08\x00\x00\x00\x00\x06\x00\x00\x007No u\
+pdates available\
+. You are using \
+the latest versi\
+on.\x07\x00\x00\x00\x07Message\x01\
+\x03\x00\x00\x00\x1a\x00N\x00o\x00t\x00h\x00i\x00\
+n\x00g\x00 \x00f\x00o\x00u\x00n\x00d\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x0dNothing\
+ found\x07\x00\x00\x00\x07Messa\
+ge\x01\x03\x00\x00\x00\x0a\x00C\x00o\x00m\x00i\
+\x00c\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05Comi\
+c\x07\x00\x00\x00\x06NlKind\x01\x03\x00\x00\
+\x00\x0c\x00D\x00o\x00u\x00j\x00i\x00n\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x06Doujin\x07\x00\
+\x00\x00\x06NlKind\x01\x03\x00\x00\x00 \x00\
+I\x00n\x00d\x00o\x00n\x00e\x00s\x00i\x00\
+a\x00n\x00 \x00c\x00o\x00m\x00i\x00c\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x10Indones\
+ian comic\x07\x00\x00\x00\x06Nl\
+Kind\x01\x03\x00\x00\x00\x0a\x00M\x00a\x00n\
+\x00g\x00a\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05Ma\
+nga\x07\x00\x00\x00\x06NlKind\x01\x03\
+\x00\x00\x00\x0c\x00M\x00a\x00n\x00h\x00u\x00a\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06Manhua\
+\x07\x00\x00\x00\x06NlKind\x01\x03\x00\x00\x00\
+\x0c\x00M\x00a\x00n\x00h\x00w\x00a\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00\x06Manhwa\x07\x00\x00\
+\x00\x06NlKind\x01\x03\x00\x00\x00\x12\x00O\
+\x00E\x00L\x00-\x00m\x00a\x00n\x00g\x00a\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x09OEL-ma\
+nga\x07\x00\x00\x00\x06NlKind\x01\x03\
+\x00\x00\x00\x0e\x00O\x00n\x00e\x00s\x00h\x00o\
+\x00t\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Ones\
+hot\x07\x00\x00\x00\x06NlKind\x01\x03\
+\x00\x00\x00\x0a\x00O\x00t\x00h\x00e\x00r\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x05Other\x07\x00\x00\
+\x00\x06NlKind\x01\x03\x00\x00\x00\x0c\x00R\
+\x00a\x00n\x00o\x00b\x00e\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x06Ranobe\x07\x00\x00\x00\x06N\
+lKind\x01\x03\x00\x00\x00\x0e\x00R\x00u\x00\
+c\x00o\x00m\x00i\x00c\x08\x00\x00\x00\x00\x06\x00\
+\x00\x00\x07Rucomic\x07\x00\x00\x00\x06N\
+lKind\x01\x03\x00\x00\x00\x0e\x00R\x00u\x00\
+m\x00a\x00n\x00g\x00a\x08\x00\x00\x00\x00\x06\x00\
+\x00\x00\x07Rumanga\x07\x00\x00\x00\x06N\
+lKind\x01\x03\x00\x00\x00\x12\x00U\x00n\x00\
+d\x00e\x00f\x00i\x00n\x00e\x00d\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00\x09Undefined\
+\x07\x00\x00\x00\x06NlKind\x01\x03\x00\x00\x00\
+\x1a\x00W\x00e\x00s\x00t\x00e\x00r\x00n\x00\
+ \x00c\x00o\x00m\x00i\x00c\x08\x00\x00\x00\x00\
+\x06\x00\x00\x00\x0dWestern com\
+ic\x07\x00\x00\x00\x06NlKind\x01\x03\x00\
+\x00\x00\x0e\x00E\x00n\x00g\x00l\x00i\x00s\x00\
+h\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Engli\
+sh\x07\x00\x00\x00\x0aNlLanguag\
+e\x01\x03\x00\x00\x00\x10\x00J\x00a\x00p\x00a\x00\
+n\x00e\x00s\x00e\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x08Japanese\x07\x00\x00\x00\x0aNl\
+Language\x01\x03\x00\x00\x00\x0e\x00R\
+\x00u\x00s\x00s\x00i\x00a\x00n\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x07Russian\x07\x00\x00\
+\x00\x0aNlLanguage\x01\x03\x00\x00\
+\x00\x12\x00U\x00k\x00r\x00a\x00i\x00n\x00i\
+\x00a\x00n\x08\x00\x00\x00\x00\x06\x00\x00\x00\x09Uk\
+rainian\x07\x00\x00\x00\x0aNlLa\
+nguage\x01\x03\x00\x00\x00\x12\x00U\x00n\
+\x00d\x00e\x00f\x00i\x00n\x00e\x00d\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x09Undefine\
+d\x07\x00\x00\x00\x0aNlLanguage\
+\x01\x03\x00\x00\x00\x10\x00C\x00h\x00a\x00p\x00t\
+\x00e\x00r\x00s\x08\x00\x00\x00\x00\x06\x00\x00\x00\x08\
+Chapters\x07\x00\x00\x00\x05Oth\
+er\x01\x03\x00\x00\x00\x08\x00P\x00a\x00g\x00e\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04Page\x07\x00\
+\x00\x00\x05Other\x01\x03\x00\x00\x00\x1e\x00P\
+\x00a\x00g\x00e\x00 \x00i\x00s\x00 \x00l\
+\x00o\x00a\x00d\x00i\x00n\x00g\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x0fPage is lo\
+ading\x07\x00\x00\x00\x05Other\x01\
+\x03\x00\x00\x00\x0c\x00R\x00a\x00t\x00i\x00n\x00\
+g\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06Ratin\
+g\x07\x00\x00\x00\x05Other\x01\x03\x00\x00\x00\
+\x0e\x00S\x00i\x00g\x00n\x00 \x00i\x00n\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x07Sign in\
+\x07\x00\x00\x00\x05Other\x01\x03\x00\x00\x00\x0c\
+\x00S\x00t\x00a\x00t\x00u\x00s\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x06Status\x07\x00\x00\x00\
+\x05Other\x01\x03\x00\x00\x00\x0e\x00V\x00o\
+\x00l\x00u\x00m\x00e\x00s\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x07Volumes\x07\x00\x00\x00\x05\
+Other\x01\x03\x00\x00\x00\x0a\x00A\x00b\x00\
+o\x00u\x00t\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05A\
+bout\x07\x00\x00\x00\x11Setting\
+sInterface\x01\x03\x00\x00\x00\x22\
+\x00A\x00p\x00p\x00l\x00i\x00c\x00a\x00t\
+\x00i\x00o\x00n\x00 \x00t\x00h\x00e\x00m\
+\x00e\x08\x00\x00\x00\x00\x06\x00\x00\x00\x11Appl\
+ication theme\x07\x00\x00\
+\x00\x11SettingsInterf\
+ace\x01\x03\x00\x00\x00L\x00A\x00u\x00t\x00\
+o\x00m\x00a\x00t\x00i\x00c\x00a\x00l\x00\
+l\x00y\x00 \x00m\x00a\x00r\x00k\x00 \x00\
+e\x00p\x00i\x00s\x00o\x00d\x00e\x00s\x00\
+ \x00a\x00s\x00 \x00w\x00a\x00t\x00c\x00\
+h\x00e\x00d\x08\x00\x00\x00\x00\x06\x00\x00\x00&A\
+utomatically mar\
+k episodes as wa\
+tched\x07\x00\x00\x00\x11Settin\
+gsInterface\x01\x03\x00\x00\x00\
+H\x00C\x00h\x00a\x00n\x00g\x00e\x00 \x00\
+t\x00h\x00e\x00 \x00a\x00p\x00p\x00e\x00\
+a\x00r\x00a\x00n\x00c\x00e\x00 \x00o\x00\
+f\x00 \x00a\x00p\x00p\x00l\x00i\x00c\x00\
+a\x00t\x00i\x00o\x00n\x08\x00\x00\x00\x00\x06\x00\
+\x00\x00$Change the ap\
+pearance of appl\
+ication\x07\x00\x00\x00\x11Sett\
+ingsInterface\x01\x03\x00\
+\x00\x00H\x00C\x00h\x00a\x00n\x00g\x00e\x00\
+ \x00t\x00h\x00e\x00 \x00s\x00i\x00z\x00\
+e\x00 \x00o\x00f\x00 \x00w\x00i\x00d\x00\
+g\x00e\x00t\x00s\x00 \x00a\x00n\x00d\x00\
+ \x00f\x00o\x00n\x00t\x00s\x08\x00\x00\x00\x00\
+\x06\x00\x00\x00$Change the \
+size of widgets \
+and fonts\x07\x00\x00\x00\x11Se\
+ttingsInterface\x01\
+\x03\x00\x00\x00r\x00C\x00h\x00a\x00n\x00g\x00\
+e\x00s\x00 \x00w\x00i\x00l\x00l\x00 \x00\
+t\x00a\x00k\x00e\x00 \x00e\x00f\x00f\x00\
+e\x00c\x00t\x00 \x00a\x00f\x00t\x00e\x00\
+r\x00 \x00r\x00e\x00s\x00t\x00a\x00r\x00\
+t\x00i\x00n\x00g\x00 \x00t\x00h\x00e\x00\
+ \x00a\x00p\x00p\x00l\x00i\x00c\x00a\x00\
+t\x00i\x00o\x00n\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+9Changes will ta\
+ke effect after \
+restarting the a\
+pplication\x07\x00\x00\x00\x11S\
+ettingsInterface\
+\x01\x03\x00\x00\x00\x22\x00C\x00h\x00e\x00c\x00k\
+\x00 \x00f\x00o\x00r\x00 \x00u\x00p\x00d\
+\x00a\x00t\x00e\x00s\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x11Check for upda\
+tes\x07\x00\x00\x00\x11Settings\
+Interface\x01\x03\x00\x00\x00Z\x00\
+C\x00h\x00e\x00c\x00k\x00 \x00f\x00o\x00\
+r\x00 \x00u\x00p\x00d\x00a\x00t\x00e\x00\
+s\x00 \x00w\x00h\x00e\x00n\x00 \x00t\x00\
+h\x00e\x00 \x00a\x00p\x00p\x00l\x00i\x00\
+c\x00a\x00t\x00i\x00o\x00n\x00 \x00s\x00\
+t\x00a\x00r\x00t\x00s\x08\x00\x00\x00\x00\x06\x00\
+\x00\x00-Check for upd\
+ates when the ap\
+plication starts\
+\x07\x00\x00\x00\x11SettingsInt\
+erface\x01\x03\x00\x00\x00\x08\x00D\x00a\
+\x00r\x00k\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04Da\
+rk\x07\x00\x00\x00\x11SettingsI\
+nterface\x01\x03\x00\x00\x00\x10\x00E\
+\x00p\x00i\x00s\x00o\x00d\x00e\x00s\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x08Episodes\
+\x07\x00\x00\x00\x11SettingsInt\
+erface\x01\x03\x00\x00\x00\x1c\x00I\x00n\
+\x00t\x00e\x00r\x00f\x00a\x00c\x00e\x00 \
+\x00z\x00o\x00o\x00m\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x0eInterface zoom\
+\x07\x00\x00\x00\x11SettingsInt\
+erface\x01\x03\x00\x00\x00\x10\x00L\x00a\
+\x00n\x00g\x00u\x00a\x00g\x00e\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x08Language\x07\x00\
+\x00\x00\x11SettingsInter\
+face\x01\x03\x00\x00\x00\x0a\x00L\x00i\x00g\
+\x00h\x00t\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05Li\
+ght\x07\x00\x00\x00\x11Settings\
+Interface\x01\x03\x00\x00\x00\x1e\x00\
+P\x00e\x00r\x00s\x00o\x00n\x00a\x00l\x00\
+i\x00z\x00a\x00t\x00i\x00o\x00n\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00\x0fPersonali\
+zation\x07\x00\x00\x00\x11Setti\
+ngsInterface\x01\x03\x00\x00\
+\x00\x22\x00P\x00r\x00o\x00j\x00e\x00c\x00t\
+\x00 \x00o\x00n\x00 \x00G\x00i\x00t\x00H\
+\x00u\x00b\x08\x00\x00\x00\x00\x06\x00\x00\x00\x11Pr\
+oject on GitHub\x07\
+\x00\x00\x00\x11SettingsInte\
+rface\x01\x03\x00\x00\x00D\x00S\x00e\x00\
+t\x00 \x00y\x00o\x00u\x00r\x00 \x00p\x00\
+r\x00e\x00f\x00e\x00r\x00r\x00e\x00d\x00\
+ \x00l\x00a\x00n\x00g\x00u\x00a\x00g\x00\
+e\x00 \x00f\x00o\x00r\x00 \x00U\x00I\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x22Set you\
+r preferred lang\
+uage for UI\x07\x00\x00\x00\x11\
+SettingsInterfac\
+e\x01\x03\x00\x00\x00\x10\x00S\x00e\x00t\x00t\x00\
+i\x00n\x00g\x00s\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x08Settings\x07\x00\x00\x00\x11Se\
+ttingsInterface\x01\
+\x03\x00\x00\x00\x1e\x00S\x00o\x00f\x00t\x00w\x00\
+a\x00r\x00e\x00 \x00u\x00p\x00d\x00a\x00\
+t\x00e\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0fSof\
+tware update\x07\x00\x00\x00\
+\x11SettingsInterfa\
+ce\x01\x03\x00\x00\x00t\x00T\x00h\x00e\x00 \
+\x00n\x00e\x00w\x00 \x00v\x00e\x00r\x00s\
+\x00i\x00o\x00n\x00 \x00w\x00i\x00l\x00l\
+\x00 \x00b\x00e\x00 \x00m\x00o\x00r\x00e\
+\x00 \x00s\x00t\x00a\x00b\x00l\x00e\x00 \
+\x00a\x00n\x00d\x00 \x00h\x00a\x00v\x00e\
+\x00 \x00m\x00o\x00r\x00e\x00 \x00f\x00e\
+\x00a\x00t\x00u\x00r\x00e\x00s\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00:The new ve\
+rsion will be mo\
+re stable and ha\
+ve more features\
+\x07\x00\x00\x00\x11SettingsInt\
+erface\x01\x03\x00\x00\x00$\x00U\x00s\
+\x00e\x00 \x00s\x00y\x00s\x00t\x00e\x00m\
+\x00 \x00s\x00e\x00t\x00t\x00i\x00n\x00g\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x12Use sy\
+stem setting\x07\x00\x00\x00\
+\x11SettingsInterfa\
+ce\x01\x03\x00\x00\x00\x0c\x00F\x00r\x00o\x00z\
+\x00e\x00n\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06Fr\
+ozen\x07\x00\x00\x00\x06Status\x01\
+\x03\x00\x00\x00\x0e\x00O\x00n\x00g\x00o\x00i\x00\
+n\x00g\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Ong\
+oing\x07\x00\x00\x00\x06Status\x01\
+\x03\x00\x00\x00\x10\x00R\x00e\x00l\x00e\x00a\x00\
+s\x00e\x00d\x08\x00\x00\x00\x00\x06\x00\x00\x00\x08R\
+eleased\x07\x00\x00\x00\x06Stat\
+us\x01\x88\x00\x00\x00\x02\x01\x01\
+\x00\x00 \x1e\
+<\
+\xb8d\x18\xca\xef\x9c\x95\xcd!\x1c\xbf`\xa1\xbd\xdd\xa7\
+\x00\x00\x00\x05ru_RUB\x00\x00\x030\x00\x04\
+\xa8\x8b\x00\x00\x17\xa0\x00\x05 D\x00\x00\x03\xe8\x00\x05\
+0\xa4\x00\x00\x01&\x00\x052\xde\x00\x00\x0b\x17\x00\x05\
+7\xfe\x00\x00\x06\x5c\x00\x05g\xd5\x00\x00\x128\x00 \
+\x7fy\x00\x00\x06\xc2\x00 \xfdw\x00\x00\x12f\x00G\
+\x96\xc4\x00\x00\x13g\x00Hw9\x00\x00\x02\x84\x00J\
+c\xf3\x00\x00\x0e\x12\x00R\xfd\xf4\x00\x00\x18\x9a\x00S\
+]\xfe\x00\x00\x01Q\x00S\x84\xd1\x00\x00\x0e\xc2\x00V\
+\x8a\xc2\x00\x00\x04j\x00V\xae\xc2\x00\x00\x0f\xac\x00X\
+\xc9\xc4\x00\x00\x05m\x00\xaa\xb4\x93\x00\x00\x01\xd9\x00\xbf\
+\xf1\xf4\x00\x00\x00x\x01'\xda.\x00\x00\x0b\xc9\x01L\
+\x05#\x00\x00\x16\x88\x01\xa5\xcc{\x00\x00\x09\x9c\x01\xac\
+\x0a~\x00\x00\x0c\xbb\x02\xf9\x88\xc9\x00\x00\x06\x22\x03{\
+\x0a~\x00\x00\x0aQ\x03\xdbsg\x00\x00\x04\x0b\x03\xdb\
+sg\x00\x00\x06\x8d\x04r\x80\xd4\x00\x00\x03\x1a\x04\x87\
+\xd7.\x00\x00\x0d\x00\x04\x98I\xbc\x00\x00\x00K\x04\xac\
+,\xa5\x00\x00\x00\xbc\x04\xb6\xc0\xfe\x00\x00\x0e>\x04\xc0\
+RN\x00\x00\x09\xfb\x04\xd9p\xbe\x00\x00\x1c$\x05\x22\
+?\xe9\x00\x00\x19{\x058O\xb1\x00\x00\x0e\xec\x058\
+O\xd1\x00\x00\x0f\x1b\x05\x88U\x85\x00\x00\x0f\xd8\x05\x88\
+\xb0G\x00\x00\x01\xaa\x05\x88\xb0G\x00\x00\x12\xb7\x05\x90\
+_\xc2\x00\x00\x08j\x05\x9b\x88\x98\x00\x00\x05\x9b\x05\xaa\
+\x8b\xc3\x00\x00\x13\x10\x05\xb5\x1b\xbe\x00\x00\x0bq\x05\xc6\
+\xa8\xa5\x00\x00\x02S\x06\x06\xf6t\x00\x00\x04:\x06!\
+\xd8#\x00\x00\x0eo\x06L\x9f4\x00\x00\x0f~\x06N\
+`\x17\x00\x00\x1cY\x06\xc2\xecl\x00\x00\x09\x0d\x07\x0a\
+^\x03\x00\x00\x17\xd6\x07(T\xe4\x00\x00\x04\x9c\x07\x9a\
+\x80\xd3\x00\x00\x16\xe7\x08N\xb2\xf5\x00\x00\x18d\x08^\
+\x80\x03\x00\x00\x07\xf2\x08hI\x95\x00\x00\x11%\x08~\
+2\x13\x00\x00\x02\xe5\x08\x80\x13^\x00\x00\x11\x90\x08\x9e\
+=\xd9\x00\x00\x06\xfa\x08\xa81s\x00\x00\x02\xb4\x08\xaa\
+\xe3\xe4\x00\x00\x01{\x08\xb1D~\x00\x00\x0a\xbe\x08\xb2\
+(\x07\x00\x00\x04\xd8\x08\xb7\xb0\x17\x00\x00\x05\x11\x08\xc2\
+\x8a\xe4\x00\x00\x05;\x09Q\xe9\xd3\x00\x00\x15-\x09\x9a\
+\xc0\x05\x00\x00\x00\x00\x09\xbac\xa3\x00\x00\x10\x05\x09\xc3\
+\x84\x81\x00\x00\x107\x09\xca\x9f.\x00\x00\x11\x5c\x09\xfe\
+\x06\xae\x00\x00\x02'\x09\xfe\x06\xae\x00\x00\x05\xc4\x09\xfe\
+\x06\xae\x00\x00\x12\xe5\x0a+w\x1e\x00\x00\x14\xa5\x0aU\
+\x903\x00\x00\x07K\x0a{z\x85\x00\x00\x1ay\x0a\xae\
+\xbe\xae\x00\x00\x18\xd3\x0a\xbc\xb8\xf4\x00\x00\x10g\x0a\xbc\
+\xb8\xf4\x00\x00\x11\xcc\x0a\xdd\x0b>\x00\x00\x15\xbf\x0bV\
+\x5c\x94\x00\x00\x13\xf9\x0b\x96v\xf4\x00\x00\x03N\x0c\x07\
+\x9cT\x00\x00\x0d\xc7\x0c\x1c\xf5$\x00\x00\x03\xaa\x0c+\
+\x8c4\x00\x00\x1c\x89\x0cN0\xd8\x00\x00\x10\xeb\x0cf\
+\xa1u\x00\x00\x00\xeb\x0c\xa8h\x87\x00\x00\x1b\xac\x0c\xbb\
+\x01s\x00\x00\x1a9\x0c\xdc\x9d-\x00\x00\x18\x12\x0c\xe4\
+\xbb\xc9\x00\x00\x09F\x0d\x03\xac\xd3\x00\x00\x03|\x0dc\
+\xc3\x93\x00\x00\x13<\x0do\x06\x14\x00\x00\x07\xa5\x0d\xa5\
+\x0f\x13\x00\x00\x10\xa5\x0e\x14\xdds\x00\x00\x08\xb6\x0e`\
+o\xf3\x00\x00\x1a\xc2\x0e\x87\xa8\x03\x00\x00\x12\x0e\x0f\x0a\
+\xb6\xd9\x00\x00\x05\xee\x0f2\x8b\xb2\x00\x00\x19$\x0fC\
+7A\x00\x00\x0fH\x0f\xaa3\xb5\x00\x00\x13\xa4i\x00\
+\x00\x1c\xb8\x03\x00\x00\x00\x1e\x04\x1a\x04>\x044\x00 \
+\x040\x042\x04B\x04>\x04@\x048\x047\x040\
+\x04F\x048\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\x12\
+Authorization co\
+de\x07\x00\x00\x00\x06Dialog\x01\x03\x00\
+\x00\x00\x0c\x04\x1e\x04B\x04<\x045\x04=\x040\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x06Cancel\x07\
+\x00\x00\x00\x06Dialog\x01\x03\x00\x00\x00\x1c\
+\x04\x1f\x04@\x04>\x04G\x048\x04B\x040\x04=\
+\x04>\x00 \x043\x04;\x040\x042\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x0dChapters r\
+ead\x07\x00\x00\x00\x06Dialog\x01\x03\
+\x00\x00\x00\x0e\x04#\x044\x040\x04;\x048\x04B\
+\x04L\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06Dele\
+te\x07\x00\x00\x00\x06Dialog\x01\x03\x00\
+\x00\x00\x18\x04\x1f\x04>\x04;\x04C\x04G\x048\x04\
+B\x04L\x00 \x04:\x04>\x044\x08\x00\x00\x00\x00\
+\x06\x00\x00\x00\x08Get code\x07\x00\x00\
+\x00\x06Dialog\x01\x03\x00\x00\x00\x0c\x04!\
+\x04?\x048\x04A\x04>\x04:\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x04List\x07\x00\x00\x00\x06Dia\
+log\x01\x03\x00\x00\x00\x0a\x04\x1b\x04>\x043\x04\
+8\x04=\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05Log\
+in\x07\x00\x00\x00\x06Dialog\x01\x03\x00\
+\x00\x00\x0c\x04\x1f\x040\x04@\x04>\x04;\x04L\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x08Passwor\
+d\x07\x00\x00\x00\x06Dialog\x01\x03\x00\x00\
+\x00\x0e\x04 \x045\x049\x04B\x048\x04=\x043\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06Rating\
+\x07\x00\x00\x00\x06Dialog\x01\x03\x00\x00\x00\
+&\x04\x1f\x04>\x04:\x040\x047\x04K\x042\x04\
+0\x04B\x04L\x00 \x04A\x04?\x04>\x049\x04\
+;\x045\x04@\x04K\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x0dShow spoilers\x07\x00\
+\x00\x00\x06Dialog\x01\x03\x00\x00\x00\x0a\x04\
+\x12\x04>\x049\x04B\x048\x08\x00\x00\x00\x00\x06\x00\
+\x00\x00\x07Sign in\x07\x00\x00\x00\x06D\
+ialog\x01\x03\x00\x00\x00\x10\x04\x1e\x041\x04\
+=\x04>\x042\x048\x04B\x04L\x08\x00\x00\x00\x00\
+\x06\x00\x00\x00\x06Update\x07\x00\x00\x00\x06\
+Dialog\x01\x03\x00\x00\x00\x12\x04\x1f\x04@\
+\x048\x04<\x045\x04=\x048\x04B\x04L\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x05Apply\x07\x00\x00\
+\x00\x04Form\x01\x03\x00\x00\x00\x10\x04\x1a\x040\
+\x04B\x040\x04;\x04>\x043\x048\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x08Catalogs\x07\x00\
+\x00\x00\x04Form\x01\x03\x00\x00\x00\x12\x04\x1f\x04\
+5\x04@\x04A\x04>\x04=\x040\x046\x048\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x0aCharact\
+ers\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\
+\x00\x12\x04\x1f\x04@\x04>\x04G\x048\x04B\x040\
+\x04=\x04>\x08\x00\x00\x00\x00\x06\x00\x00\x00\x09Co\
+mpleted\x07\x00\x00\x00\x04Form\
+\x01\x03\x00\x00\x00\x0e\x04\x11\x04@\x04>\x04H\x045\
+\x04=\x04>\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Dr\
+opped\x07\x00\x00\x00\x04Form\x01\x03\
+\x00\x00\x00\x0e\x04$\x048\x04;\x04L\x04B\x04@\
+\x04K\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Filt\
+ers\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\
+\x00\x1a\x04!\x04?\x048\x04A\x04>\x04:\x00 \
+\x046\x040\x04=\x04@\x04>\x042\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x0bGenres lis\
+t\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\x06\
+\x04\x22\x048\x04?\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04\
+Kind\x07\x00\x00\x00\x04Form\x01\x03\x00\
+\x00\x00\x0c\x00N\x00l\x00i\x00g\x00h\x00t\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x0aMainWin\
+dow\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\
+\x00\x10\x04\x1e\x04B\x04;\x04>\x046\x045\x04=\
+\x04>\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07On h\
+old\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\
+\x00\x14\x04!\x04>\x04@\x04B\x048\x04@\x04>\
+\x042\x04:\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05\
+Order\x07\x00\x00\x00\x04Form\x01\x03\
+\x00\x00\x00\x1c\x04\x17\x040\x04?\x04;\x040\x04=\
+\x048\x04@\x04>\x042\x040\x04=\x04=\x04>\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Planne\
+d\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\x16\
+\x04\x1f\x045\x04@\x045\x04G\x048\x04B\x04K\
+\x042\x040\x04N\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0a\
+Re-reading\x07\x00\x00\x00\x04F\
+orm\x01\x03\x00\x00\x00\x0a\x04'\x048\x04B\x04\
+0\x04N\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Rea\
+ding\x07\x00\x00\x00\x04Form\x01\x03\x00\
+\x00\x00\x12\x04!\x042\x04O\x047\x040\x04=\x04\
+=\x04>\x045\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07R\
+elated\x07\x00\x00\x00\x04Form\x01\
+\x03\x00\x00\x00\x10\x04!\x041\x04@\x04>\x04A\x04\
+8\x04B\x04L\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05R\
+eset\x07\x00\x00\x00\x04Form\x01\x03\x00\
+\x00\x00\x0a\x04\x1f\x04>\x048\x04A\x04:\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00\x06Search\x07\x00\x00\
+\x00\x04Form\x01\x03\x00\x00\x00\x0a\x04\x12\x04>\
+\x049\x04B\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07\
+Sign in\x07\x00\x00\x00\x04Form\
+\x01\x03\x00\x00\x00\x0e\x04\x18\x04A\x04B\x04>\x04@\
+\x048\x04O\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Hi\
+story\x07\x00\x00\x00\x0aMainWi\
+ndow\x01\x03\x00\x00\x00\x14\x04\x11\x048\x041\
+\x04;\x048\x04>\x04B\x045\x04:\x040\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x07Library\x07\
+\x00\x00\x00\x0aMainWindow\x01\x03\
+\x00\x00\x00\x0e\x04\x13\x04;\x040\x042\x04=\x040\
+\x04O\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04Main\
+\x07\x00\x00\x00\x0aMainWindow\x01\
+\x03\x00\x00\x00\x0c\x00N\x00l\x00i\x00g\x00h\x00\
+t\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0aMainW\
+indow\x07\x00\x00\x00\x0aMainWi\
+ndow\x01\x03\x00\x00\x00\x10\x04(\x048\x04:\
+\x048\x04<\x04>\x04@\x048\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x09Shikimori\x07\x00\x00\
+\x00\x0aMainWindow\x01\x03\x00\x00\
+\x00*\x04\x14\x04>\x041\x040\x042\x048\x04B\
+\x04L\x00 \x042\x00 \x041\x048\x041\x04;\
+\x048\x04>\x04B\x045\x04:\x04C\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x0eAdd to Lib\
+rary\x07\x00\x00\x00\x04Menu\x01\x03\x00\
+\x00\x000\x04\x1e\x04G\x048\x04A\x04B\x048\x04\
+B\x04L\x00 \x04;\x04>\x04:\x040\x04;\x04\
+L\x04=\x04K\x045\x00 \x04D\x040\x049\x04\
+;\x04K\x08\x00\x00\x00\x00\x06\x00\x00\x00\x11Cle\
+ar local files\x07\x00\
+\x00\x00\x04Menu\x01\x03\x00\x00\x00(\x04\x1e\x04\
+B\x04<\x045\x04B\x048\x04B\x04L\x00 \x04\
+?\x04@\x04>\x04G\x048\x04B\x040\x04=\x04\
+=\x04K\x04<\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0cM\
+ark as read\x07\x00\x00\x00\x04\
+Menu\x01\x03\x00\x00\x00F\x04\x1e\x04B\x04<\
+\x045\x04B\x048\x04B\x04L\x00 \x04?\x04@\
+\x04>\x04G\x048\x04B\x040\x04=\x04=\x04K\
+\x04<\x00 \x042\x04A\x045\x00 \x04?\x04@\
+\x045\x044\x04K\x044\x04C\x04I\x048\x045\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x19Mark a\
+s read all previ\
+ous\x07\x00\x00\x00\x04Menu\x01\x03\x00\x00\
+\x00$\x04\x1e\x04B\x04:\x04@\x04K\x04B\x04L\
+\x00 \x042\x00 \x041\x04@\x040\x04C\x047\
+\x045\x04@\x045\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0f\
+Open in browser\x07\
+\x00\x00\x00\x04Menu\x01\x03\x00\x00\x00.\x04\x1e\
+\x04B\x04:\x04@\x04K\x04B\x04L\x00 \x04;\
+\x04>\x04:\x040\x04;\x04L\x04=\x04K\x045\
+\x00 \x04D\x040\x049\x04;\x04K\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x10Open local\
+ files\x07\x00\x00\x00\x04Menu\x01\
+\x03\x00\x00\x00\x16\x04#\x044\x040\x04;\x048\x04\
+B\x04L\x00 \x042\x04A\x045\x08\x00\x00\x00\x00\
+\x06\x00\x00\x00\x0aRemove all\x07\
+\x00\x00\x00\x04Menu\x01\x03\x00\x00\x00*\x04#\
+\x044\x040\x04;\x048\x04B\x04L\x00 \x048\
+\x047\x00 \x041\x048\x041\x04;\x048\x04>\
+\x04B\x045\x04:\x048\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x13Remove from li\
+brary\x07\x00\x00\x00\x04Menu\x01\x03\
+\x00\x00\x006\x04#\x044\x040\x04;\x048\x04B\
+\x04L\x00 \x04>\x04B\x04<\x045\x04B\x04:\
+\x04C\x00 \x04>\x00 \x04?\x04@\x04>\x04G\
+\x04B\x045\x04=\x048\x048\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x10Remove read \
+mark\x07\x00\x00\x00\x04Menu\x01\x03\x00\
+\x00\x00(\x04\x1f\x04@\x04>\x042\x045\x04@\x04\
+:\x040\x00 \x04>\x041\x04=\x04>\x042\x04\
+;\x045\x04=\x048\x049\x00.\x08\x00\x00\x00\x00\
+\x06\x00\x00\x00\x12Check for u\
+pdates.\x07\x00\x00\x00\x07Mess\
+age\x01\x03\x00\x00\x006\x04\x1e\x04H\x048\x04\
+1\x04:\x040\x00 \x04?\x04@\x04>\x042\x04\
+5\x04@\x04:\x048\x00 \x04>\x041\x04=\x04\
+>\x042\x04;\x045\x04=\x048\x049\x00.\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x1bError c\
+hecking for upda\
+tes.\x07\x00\x00\x00\x07Message\
+\x01\x03\x00\x00\x00\x22\x04$\x040\x049\x04;\x04K\
+\x00 \x00{\x00}\x00 \x04C\x044\x040\x04;\
+\x045\x04=\x04K\x00.\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x1bFiles {} have \
+been removed.\x07\x00\x00\
+\x00\x07Message\x01\x03\x00\x00\x00&\x04\
+\x1c\x040\x04=\x043\x040\x00 \x00{\x00}\x00\
+ \x044\x04>\x041\x040\x042\x04;\x045\x04\
+=\x040\x00.\x08\x00\x00\x00\x00\x06\x00\x00\x00\x18M\
+anga {} has been\
+ added.\x07\x00\x00\x00\x07Mess\
+age\x01\x03\x00\x00\x00\x22\x04\x1c\x040\x04=\x04\
+3\x040\x00 \x00{\x00}\x00 \x04C\x044\x04\
+0\x04;\x045\x04=\x040\x00.\x08\x00\x00\x00\x00\
+\x06\x00\x00\x00\x1aManga {} ha\
+s been deleted.\x07\
+\x00\x00\x00\x07Message\x01\x03\x00\x00\x00\
+\x88\x04\x1d\x04>\x042\x040\x04O\x00 \x042\x04\
+5\x04@\x04A\x048\x04O\x00 \x00{\x00r\x00\
+e\x00s\x00u\x00l\x00t\x00}\x00 \x044\x04\
+>\x04A\x04B\x04C\x04?\x04=\x040\x00!\x00\
+ \x04\x12\x04K\x00 \x048\x04A\x04?\x04>\x04\
+;\x04L\x047\x04C\x045\x04B\x045\x00 \x04\
+2\x045\x04@\x04A\x048\x04N\x00 \x00{\x00\
+A\x00P\x00P\x00_\x00V\x00E\x00R\x00S\x00\
+I\x00O\x00N\x00}\x00.\x08\x00\x00\x00\x00\x06\x00\
+\x00\x00NNew version {\
+result} is avail\
+able! You are cu\
+rrently on versi\
+on {APP_VERSION}\
+.\x07\x00\x00\x00\x07Message\x01\x03\x00\
+\x00\x00\x1c\x04\x1d\x045\x04B\x00 \x04A\x04>\x04\
+5\x044\x048\x04=\x045\x04=\x048\x04O\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x0dNo conn\
+ection\x07\x00\x00\x00\x07Messa\
+ge\x01\x03\x00\x00\x00t\x04\x1d\x045\x04B\x00 \
+\x044\x04>\x04A\x04B\x04C\x04?\x04=\x04K\
+\x04E\x00 \x04>\x041\x04=\x04>\x042\x04;\
+\x045\x04=\x048\x049\x00.\x00 \x04\x12\x04K\
+\x00 \x048\x04A\x04?\x04>\x04;\x04L\x047\
+\x04C\x045\x04B\x045\x00 \x04?\x04>\x04A\
+\x04;\x045\x044\x04=\x04N\x04N\x00 \x042\
+\x045\x04@\x04A\x048\x04N\x00.\x08\x00\x00\x00\
+\x00\x06\x00\x00\x007No updates\
+ available. You \
+are using the la\
+test version.\x07\x00\x00\
+\x00\x07Message\x01\x03\x00\x00\x00\x22\x04\
+\x1d\x048\x04G\x045\x043\x04>\x00 \x04=\x04\
+5\x00 \x04=\x040\x049\x044\x045\x04=\x04\
+>\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0dNothi\
+ng found\x07\x00\x00\x00\x07Mes\
+sage\x01\x03\x00\x00\x00\x0c\x04\x1a\x04>\x04<\
+\x048\x04:\x04A\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05\
+Comic\x07\x00\x00\x00\x06NlKind\
+\x01\x03\x00\x00\x00\x10\x04\x14\x04>\x044\x047\x048\
+\x04=\x04A\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06\
+Doujin\x07\x00\x00\x00\x06NlKin\
+d\x01\x03\x00\x00\x00(\x04\x18\x04=\x044\x04>\x04\
+=\x045\x047\x048\x049\x04A\x04:\x048\x04\
+9\x00 \x04:\x04>\x04<\x048\x04:\x04A\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x10Indones\
+ian comic\x07\x00\x00\x00\x06Nl\
+Kind\x01\x03\x00\x00\x00\x0a\x04\x1c\x040\x04=\
+\x043\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05Ma\
+nga\x07\x00\x00\x00\x06NlKind\x01\x03\
+\x00\x00\x00\x0e\x04\x1c\x040\x04=\x04L\x04E\x04C\
+\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06Manh\
+ua\x07\x00\x00\x00\x06NlKind\x01\x03\x00\
+\x00\x00\x0c\x04\x1c\x040\x04=\x04E\x042\x040\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x06Manhwa\x07\
+\x00\x00\x00\x06NlKind\x01\x03\x00\x00\x00\x12\
+\x00O\x00E\x00L\x00-\x04<\x040\x04=\x043\
+\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\x09OEL-\
+manga\x07\x00\x00\x00\x06NlKind\
+\x01\x03\x00\x00\x00\x0c\x04\x12\x040\x04=\x04H\x04>\
+\x04B\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Ones\
+hot\x07\x00\x00\x00\x06NlKind\x01\x03\
+\x00\x00\x00\x0c\x04\x14\x04@\x04C\x043\x04>\x045\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05Other\x07\
+\x00\x00\x00\x06NlKind\x01\x03\x00\x00\x00\x0c\
+\x04 \x040\x04=\x04>\x041\x04M\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x06Ranobe\x07\x00\x00\x00\
+\x06NlKind\x01\x03\x00\x00\x00\x10\x04 \x04\
+C\x04:\x04>\x04<\x048\x04:\x04A\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00\x07Rucomic\x07\x00\
+\x00\x00\x06NlKind\x01\x03\x00\x00\x00\x0e\x04\
+ \x04C\x04<\x040\x04=\x043\x040\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00\x07Rumanga\x07\x00\
+\x00\x00\x06NlKind\x01\x03\x00\x00\x00\x1a\x04\
+\x1d\x045\x00 \x04>\x04?\x04@\x045\x044\x04\
+5\x04;\x045\x04=\x04>\x08\x00\x00\x00\x00\x06\x00\
+\x00\x00\x09Undefined\x07\x00\x00\x00\
+\x06NlKind\x01\x03\x00\x00\x00\x1e\x04\x17\x04\
+0\x04?\x040\x044\x04=\x04K\x049\x00 \x04\
+:\x04>\x04<\x048\x04:\x04A\x08\x00\x00\x00\x00\
+\x06\x00\x00\x00\x0dWestern com\
+ic\x07\x00\x00\x00\x06NlKind\x01\x03\x00\
+\x00\x00\x14\x04\x10\x04=\x043\x04;\x048\x049\x04\
+A\x04:\x048\x049\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x07English\x07\x00\x00\x00\x0aNlL\
+anguage\x01\x03\x00\x00\x00\x10\x04/\x04\
+?\x04>\x04=\x04A\x04:\x048\x049\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00\x08Japanese\x07\
+\x00\x00\x00\x0aNlLanguage\x01\x03\
+\x00\x00\x00\x0e\x04 \x04C\x04A\x04A\x04:\x048\
+\x049\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Russ\
+ian\x07\x00\x00\x00\x0aNlLangua\
+ge\x01\x03\x00\x00\x00\x14\x04#\x04:\x04@\x040\
+\x048\x04=\x04A\x04:\x048\x049\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x09Ukrainian\x07\
+\x00\x00\x00\x0aNlLanguage\x01\x03\
+\x00\x00\x00\x1a\x04\x1d\x045\x00 \x04>\x04?\x04@\
+\x045\x044\x045\x04;\x045\x04=\x04>\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x09Undefine\
+d\x07\x00\x00\x00\x0aNlLanguage\
+\x01\x03\x00\x00\x00\x08\x04\x13\x04;\x040\x042\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x08Chapters\
+\x07\x00\x00\x00\x05Other\x01\x03\x00\x00\x00\x10\
+\x04!\x04B\x04@\x040\x04=\x048\x04F\x040\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04Page\x07\x00\
+\x00\x00\x05Other\x01\x03\x00\x00\x00(\x04!\
+\x04B\x04@\x040\x04=\x048\x04F\x040\x00 \
+\x047\x040\x043\x04@\x04C\x046\x040\x045\
+\x04B\x04A\x04O\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0f\
+Page is loading\x07\
+\x00\x00\x00\x05Other\x01\x03\x00\x00\x00\x0e\x04\
+ \x045\x049\x04B\x048\x04=\x043\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00\x06Rating\x07\x00\x00\
+\x00\x05Other\x01\x03\x00\x00\x00\x0a\x04\x12\x04\
+>\x049\x04B\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x07Sign in\x07\x00\x00\x00\x05Oth\
+er\x01\x03\x00\x00\x00\x0c\x04!\x04B\x040\x04B\
+\x04C\x04A\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06St\
+atus\x07\x00\x00\x00\x05Other\x01\x03\
+\x00\x00\x00\x0a\x04\x22\x04>\x04<\x04>\x042\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x07Volumes\x07\
+\x00\x00\x00\x05Other\x01\x03\x00\x00\x00\x12\x04\
+\x1e\x00 \x04?\x04@\x04>\x045\x04:\x04B\x04\
+5\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05About\
+\x07\x00\x00\x00\x11SettingsInt\
+erface\x01\x03\x00\x00\x00\x1e\x04\x22\x045\
+\x04<\x040\x00 \x04?\x04@\x048\x04;\x04>\
+\x046\x045\x04=\x048\x04O\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x11Application \
+theme\x07\x00\x00\x00\x11Settin\
+gsInterface\x01\x03\x00\x00\x00\
+`\x04\x10\x042\x04B\x04>\x04<\x040\x04B\x04\
+8\x04G\x045\x04A\x04:\x048\x00 \x04>\x04\
+B\x04<\x045\x04G\x040\x04B\x04L\x00 \x04\
+M\x04?\x048\x047\x04>\x044\x04K\x00 \x04\
+:\x040\x04:\x00 \x04?\x04@\x04>\x04A\x04\
+<\x04>\x04B\x04@\x045\x04=\x04=\x04K\x04\
+5\x08\x00\x00\x00\x00\x06\x00\x00\x00&Autom\
+atically mark ep\
+isodes as watche\
+d\x07\x00\x00\x00\x11SettingsIn\
+terface\x01\x03\x00\x00\x00>\x04\x18\x04\
+7\x04<\x045\x04=\x048\x04B\x04L\x00 \x04\
+2\x04=\x045\x04H\x04=\x048\x049\x00 \x04\
+2\x048\x044\x00 \x04?\x04@\x048\x04;\x04\
+>\x046\x045\x04=\x048\x04O\x08\x00\x00\x00\x00\
+\x06\x00\x00\x00$Change the \
+appearance of ap\
+plication\x07\x00\x00\x00\x11Se\
+ttingsInterface\x01\
+\x03\x00\x00\x00H\x04\x18\x047\x04<\x045\x04=\x04\
+5\x04=\x048\x045\x00 \x04@\x040\x047\x04\
+<\x045\x04@\x040\x00 \x042\x048\x044\x04\
+6\x045\x04B\x04>\x042\x00 \x048\x00 \x04\
+H\x04@\x048\x04D\x04B\x04>\x042\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00$Change th\
+e size of widget\
+s and fonts\x07\x00\x00\x00\x11\
+SettingsInterfac\
+e\x01\x03\x00\x00\x00j\x04\x18\x047\x04<\x045\x04\
+=\x045\x04=\x048\x04O\x00 \x042\x04A\x04\
+B\x04C\x04?\x04O\x04B\x00 \x042\x00 \x04\
+A\x048\x04;\x04C\x00 \x04?\x04>\x04A\x04\
+;\x045\x00 \x04?\x045\x04@\x045\x047\x04\
+0\x04?\x04C\x04A\x04:\x040\x00 \x04?\x04\
+@\x048\x04;\x04>\x046\x045\x04=\x048\x04\
+O\x08\x00\x00\x00\x00\x06\x00\x00\x009Chang\
+es will take eff\
+ect after restar\
+ting the applica\
+tion\x07\x00\x00\x00\x11Setting\
+sInterface\x01\x03\x00\x00\x00(\
+\x04\x1f\x04@\x04>\x042\x045\x04@\x048\x04B\
+\x04L\x00 \x04>\x041\x04=\x04>\x042\x04;\
+\x045\x04=\x048\x04O\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x11Check for upda\
+tes\x07\x00\x00\x00\x11Settings\
+Interface\x01\x03\x00\x00\x00f\x04\
+\x1f\x04@\x04>\x042\x045\x04@\x04O\x04B\x04\
+L\x00 \x04=\x040\x04;\x048\x04G\x048\x04\
+5\x00 \x04>\x041\x04=\x04>\x042\x04;\x04\
+5\x04=\x048\x049\x00 \x04?\x04@\x048\x00\
+ \x047\x040\x04?\x04C\x04A\x04:\x045\x00\
+ \x04?\x04@\x048\x04;\x04>\x046\x045\x04\
+=\x048\x04O\x08\x00\x00\x00\x00\x06\x00\x00\x00-C\
+heck for updates\
+ when the applic\
+ation starts\x07\x00\x00\x00\
+\x11SettingsInterfa\
+ce\x01\x03\x00\x00\x00\x0c\x04\x22\x045\x04<\x04=\
+\x040\x04O\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04Da\
+rk\x07\x00\x00\x00\x11SettingsI\
+nterface\x01\x03\x00\x00\x00\x0e\x04-\
+\x04?\x048\x047\x04>\x044\x04K\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x08Episodes\x07\x00\
+\x00\x00\x11SettingsInter\
+face\x01\x03\x00\x00\x00\x1e\x04\x1c\x040\x04A\
+\x04H\x04B\x040\x041\x048\x04@\x04>\x042\
+\x040\x04=\x048\x045\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x0eInterface zoom\
+\x07\x00\x00\x00\x11SettingsInt\
+erface\x01\x03\x00\x00\x00\x08\x04/\x047\
+\x04K\x04:\x08\x00\x00\x00\x00\x06\x00\x00\x00\x08La\
+nguage\x07\x00\x00\x00\x11Setti\
+ngsInterface\x01\x03\x00\x00\
+\x00\x0e\x04!\x042\x045\x04B\x04;\x040\x04O\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05Light\x07\
+\x00\x00\x00\x11SettingsInte\
+rface\x01\x03\x00\x00\x00\x1c\x04\x1f\x045\x04\
+@\x04A\x04>\x04=\x040\x04;\x048\x047\x04\
+0\x04F\x048\x04O\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x0fPersonalization\
+\x07\x00\x00\x00\x11SettingsInt\
+erface\x01\x03\x00\x00\x00 \x04\x1f\x04@\
+\x04>\x045\x04:\x04B\x00 \x04=\x040\x00 \
+\x00G\x00i\x00t\x00H\x00u\x00b\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x11Project on\
+ GitHub\x07\x00\x00\x00\x11Sett\
+ingsInterface\x01\x03\x00\
+\x00\x00v\x04#\x04A\x04B\x040\x04=\x04>\x04\
+2\x048\x04B\x045\x00 \x04?\x04@\x045\x04\
+4\x04?\x04>\x04G\x048\x04B\x040\x045\x04\
+<\x04K\x049\x00 \x04O\x047\x04K\x04:\x00\
+ \x04?\x04>\x04;\x04L\x047\x04>\x042\x04\
+0\x04B\x045\x04;\x04L\x04A\x04:\x04>\x04\
+3\x04>\x00 \x048\x04=\x04B\x045\x04@\x04\
+D\x045\x049\x04A\x040\x08\x00\x00\x00\x00\x06\x00\
+\x00\x00\x22Set your pref\
+erred language f\
+or UI\x07\x00\x00\x00\x11Settin\
+gsInterface\x01\x03\x00\x00\x00\
+\x12\x04\x1f\x040\x04@\x040\x04<\x045\x04B\x04\
+@\x04K\x08\x00\x00\x00\x00\x06\x00\x00\x00\x08Set\
+tings\x07\x00\x00\x00\x11Settin\
+gsInterface\x01\x03\x00\x00\x00\
+\x14\x04\x1e\x041\x04=\x04>\x042\x04;\x045\x04\
+=\x048\x04O\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0fS\
+oftware update\x07\x00\
+\x00\x00\x11SettingsInter\
+face\x01\x03\x00\x00\x00\x8a\x04\x1d\x04>\x042\
+\x040\x04O\x00 \x042\x045\x04@\x04A\x048\
+\x04O\x00 \x041\x04C\x044\x045\x04B\x00 \
+\x041\x04>\x04;\x045\x045\x00 \x04A\x04B\
+\x040\x041\x048\x04;\x04L\x04=\x04>\x049\
+\x00 \x048\x00 \x041\x04C\x044\x045\x04B\
+\x00 \x048\x04<\x045\x04B\x04L\x00 \x041\
+\x04>\x04;\x04L\x04H\x045\x00 \x042\x04>\
+\x047\x04<\x04>\x046\x04=\x04>\x04A\x04B\
+\x045\x049\x08\x00\x00\x00\x00\x06\x00\x00\x00:Th\
+e new version wi\
+ll be more stabl\
+e and have more \
+features\x07\x00\x00\x00\x11Set\
+tingsInterface\x01\x03\
+\x00\x00\x00@\x04\x18\x04A\x04?\x04>\x04;\x04L\
+\x047\x04>\x042\x040\x04B\x04L\x00 \x04A\
+\x048\x04A\x04B\x045\x04<\x04=\x04K\x045\
+\x00 \x04=\x040\x04A\x04B\x04@\x04>\x049\
+\x04:\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\x12Us\
+e system setting\
+\x07\x00\x00\x00\x11SettingsInt\
+erface\x01\x03\x00\x00\x00\x14\x04\x17\x040\
+\x04<\x04>\x04@\x04>\x046\x045\x04=\x04>\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06Frozen\
+\x07\x00\x00\x00\x06Status\x01\x03\x00\x00\x00\
+\x0e\x04\x12\x04K\x04E\x04>\x044\x048\x04B\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x07Ongoing\
+\x07\x00\x00\x00\x06Status\x01\x03\x00\x00\x00\
+\x0c\x04\x18\x047\x044\x040\x04=\x04>\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00\x08Released\x07\
+\x00\x00\x00\x06Status\x01\x88\x00\x00\x00\x0d\
+\x11\x01\xfd)\x0b\xff\x14\x02\x04\xfd,\x0a\x13\
+\x00\x00 \x14\
+<\
+\xb8d\x18\xca\xef\x9c\x95\xcd!\x1c\xbf`\xa1\xbd\xdd\xa7\
+\x00\x00\x00\x05uk_UAB\x00\x00\x030\x00\x04\
+\xa8\x8b\x00\x00\x17\xc0\x00\x05 D\x00\x00\x03\xfa\x00\x05\
+0\xa4\x00\x00\x018\x00\x052\xde\x00\x00\x0b7\x00\x05\
+7\xfe\x00\x00\x06p\x00\x05g\xd5\x00\x00\x12\x5c\x00 \
+\x7fy\x00\x00\x06\xd6\x00 \xfdw\x00\x00\x12\x8a\x00G\
+\x96\xc4\x00\x00\x13\x93\x00Hw9\x00\x00\x02\x98\x00J\
+c\xf3\x00\x00\x0e8\x00R\xfd\xf4\x00\x00\x18\xb4\x00S\
+]\xfe\x00\x00\x01e\x00S\x84\xd1\x00\x00\x0e\xea\x00V\
+\x8a\xc2\x00\x00\x04\x80\x00V\xae\xc2\x00\x00\x0f\xd4\x00X\
+\xc9\xc4\x00\x00\x05y\x00\xaa\xb4\x93\x00\x00\x01\xed\x00\xbf\
+\xf1\xf4\x00\x00\x00\x80\x01'\xda.\x00\x00\x0b\xe5\x01L\
+\x05#\x00\x00\x16\xa2\x01\xa5\xcc{\x00\x00\x09\xb2\x01\xac\
+\x0a~\x00\x00\x0c\xdb\x02\xf9\x88\xc9\x00\x00\x066\x03{\
+\x0a~\x00\x00\x0ao\x03\xdbsg\x00\x00\x04\x1d\x03\xdb\
+sg\x00\x00\x06\xa1\x04r\x80\xd4\x00\x00\x03.\x04\x87\
+\xd7.\x00\x00\x0d\x22\x04\x98I\xbc\x00\x00\x00K\x04\xac\
+,\xa5\x00\x00\x00\xcc\x04\xb6\xc0\xfe\x00\x00\x0ed\x04\xc0\
+RN\x00\x00\x0a\x1b\x04\xd9p\xbe\x00\x00\x1c\x18\x05\x22\
+?\xe9\x00\x00\x19\x93\x058O\xb1\x00\x00\x0f\x14\x058\
+O\xd1\x00\x00\x0fC\x05\x88U\x85\x00\x00\x0f\xfc\x05\x88\
+\xb0G\x00\x00\x01\xbe\x05\x88\xb0G\x00\x00\x12\xe1\x05\x90\
+_\xc2\x00\x00\x08|\x05\x9b\x88\x98\x00\x00\x05\xad\x05\xaa\
+\x8b\xc3\x00\x00\x13<\x05\xb5\x1b\xbe\x00\x00\x0b\x8b\x05\xc6\
+\xa8\xa5\x00\x00\x02i\x06\x06\xf6t\x00\x00\x04L\x06!\
+\xd8#\x00\x00\x0e\x95\x06L\x9f4\x00\x00\x0f\xa6\x06N\
+`\x17\x00\x00\x1cM\x06\xc2\xecl\x00\x00\x09!\x07\x0a\
+^\x03\x00\x00\x17\xf4\x07(T\xe4\x00\x00\x04\xb2\x07\x9a\
+\x80\xd3\x00\x00\x17\x01\x08N\xb2\xf5\x00\x00\x18~\x08^\
+\x80\x03\x00\x00\x08\x04\x08hI\x95\x00\x00\x11G\x08~\
+2\x13\x00\x00\x02\xf9\x08\x80\x13^\x00\x00\x11\xb6\x08\x9e\
+=\xd9\x00\x00\x07\x0e\x08\xa81s\x00\x00\x02\xc8\x08\xaa\
+\xe3\xe4\x00\x00\x01\x8f\x08\xb1D~\x00\x00\x0a\xdc\x08\xb2\
+(\x07\x00\x00\x04\xe8\x08\xb7\xb0\x17\x00\x00\x05\x1d\x08\xc2\
+\x8a\xe4\x00\x00\x05G\x09Q\xe9\xd3\x00\x00\x15S\x09\x9a\
+\xc0\x05\x00\x00\x00\x00\x09\xbac\xa3\x00\x00\x10)\x09\xc3\
+\x84\x81\x00\x00\x10[\x09\xca\x9f.\x00\x00\x11~\x09\xfe\
+\x06\xae\x00\x00\x02;\x09\xfe\x06\xae\x00\x00\x05\xd6\x09\xfe\
+\x06\xae\x00\x00\x13\x0f\x0a+w\x1e\x00\x00\x14\xc7\x0aU\
+\x903\x00\x00\x07]\x0a{z\x85\x00\x00\x1au\x0a\xae\
+\xbe\xae\x00\x00\x18\xeb\x0a\xbc\xb8\xf4\x00\x00\x10\x8b\x0a\xbc\
+\xb8\xf4\x00\x00\x11\xf2\x0a\xdd\x0b>\x00\x00\x15\xdf\x0bV\
+\x5c\x94\x00\x00\x14#\x0b\x96v\xf4\x00\x00\x03b\x0c\x07\
+\x9cT\x00\x00\x0d\xeb\x0c\x1c\xf5$\x00\x00\x03\xbc\x0c+\
+\x8c4\x00\x00\x1c\x7f\x0cN0\xd8\x00\x00\x11\x0d\x0cf\
+\xa1u\x00\x00\x00\xfd\x0c\xa8h\x87\x00\x00\x1b\x96\x0c\xbb\
+\x01s\x00\x00\x1a5\x0c\xdc\x9d-\x00\x00\x180\x0c\xe4\
+\xbb\xc9\x00\x00\x09\x5c\x0d\x03\xac\xd3\x00\x00\x03\x8e\x0dc\
+\xc3\x93\x00\x00\x13h\x0do\x06\x14\x00\x00\x07\xb5\x0d\xa5\
+\x0f\x13\x00\x00\x10\xc7\x0e\x14\xdds\x00\x00\x08\xca\x0e`\
+o\xf3\x00\x00\x1a\xbc\x0e\x87\xa8\x03\x00\x00\x122\x0f\x0a\
+\xb6\xd9\x00\x00\x06\x02\x0f2\x8b\xb2\x00\x00\x19<\x0fC\
+7A\x00\x00\x0fp\x0f\xaa3\xb5\x00\x00\x13\xd2i\x00\
+\x00\x1c\xae\x03\x00\x00\x00\x1e\x04\x1a\x04>\x044\x00 \
+\x040\x042\x04B\x04>\x04@\x048\x047\x040\
+\x04F\x04V\x04W\x08\x00\x00\x00\x00\x06\x00\x00\x00\x12\
+Authorization co\
+de\x07\x00\x00\x00\x06Dialog\x01\x03\x00\
+\x00\x00\x14\x04!\x04:\x040\x04A\x04C\x042\x04\
+0\x04=\x04=\x04O\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x06Cancel\x07\x00\x00\x00\x06Dial\
+og\x01\x03\x00\x00\x00$\x04\x1f\x04@\x04>\x04G\
+\x048\x04B\x040\x04=\x04>\x00 \x04@\x04>\
+\x047\x044\x04V\x04;\x04V\x042\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x0dChapters r\
+ead\x07\x00\x00\x00\x06Dialog\x01\x03\
+\x00\x00\x00\x10\x04\x12\x048\x04;\x04C\x04G\x048\
+\x04B\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06De\
+lete\x07\x00\x00\x00\x06Dialog\x01\
+\x03\x00\x00\x00\x18\x04\x1e\x04B\x04@\x048\x04<\x04\
+0\x04B\x048\x00 \x04:\x04>\x044\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00\x08Get code\x07\
+\x00\x00\x00\x06Dialog\x01\x03\x00\x00\x00\x0e\
+\x04\x1f\x045\x04@\x045\x04;\x04V\x04:\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x04List\x07\x00\x00\x00\
+\x06Dialog\x01\x03\x00\x00\x00\x0a\x04\x1b\x04\
+>\x043\x04V\x04=\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x05Login\x07\x00\x00\x00\x06Dialo\
+g\x01\x03\x00\x00\x00\x0c\x04\x1f\x040\x04@\x04>\x04\
+;\x04L\x08\x00\x00\x00\x00\x06\x00\x00\x00\x08Pas\
+sword\x07\x00\x00\x00\x06Dialog\
+\x01\x03\x00\x00\x00\x0e\x04 \x045\x049\x04B\x048\
+\x04=\x043\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06Ra\
+ting\x07\x00\x00\x00\x06Dialog\x01\
+\x03\x00\x00\x00&\x04\x1f\x04>\x04:\x040\x047\x04\
+C\x042\x040\x04B\x048\x00 \x04A\x04?\x04\
+>\x049\x04;\x045\x04@\x048\x08\x00\x00\x00\x00\
+\x06\x00\x00\x00\x0dShow spoile\
+rs\x07\x00\x00\x00\x06Dialog\x01\x03\x00\
+\x00\x00\x0c\x04#\x042\x04V\x049\x04B\x048\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x07Sign in\
+\x07\x00\x00\x00\x06Dialog\x01\x03\x00\x00\x00\
+\x0e\x04\x1e\x04=\x04>\x042\x048\x04B\x048\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x06Update\x07\
+\x00\x00\x00\x06Dialog\x01\x03\x00\x00\x00\x12\
+\x04\x1f\x04@\x048\x04<\x045\x04=\x048\x04B\
+\x04L\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05Appl\
+y\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\x10\
+\x04\x1a\x040\x04B\x040\x04;\x04>\x043\x048\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x08Catalo\
+gs\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\
+\x12\x04\x1f\x045\x04@\x04A\x04>\x04=\x040\x04\
+6\x04V\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0aCha\
+racters\x07\x00\x00\x00\x04Form\
+\x01\x03\x00\x00\x00\x12\x04\x1f\x04@\x04>\x04G\x048\
+\x04B\x040\x04=\x04>\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x09Completed\x07\x00\x00\x00\x04\
+Form\x01\x03\x00\x00\x00\x0c\x04\x1a\x048\x04=\
+\x04C\x04B\x04>\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07\
+Dropped\x07\x00\x00\x00\x04Form\
+\x01\x03\x00\x00\x00\x0e\x04$\x04V\x04;\x04L\x04B\
+\x04@\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Fi\
+lters\x07\x00\x00\x00\x04Form\x01\x03\
+\x00\x00\x00\x1a\x04!\x04?\x048\x04A\x04>\x04:\
+\x00 \x046\x040\x04=\x04@\x04V\x042\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x0bGenres l\
+ist\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\
+\x00\x06\x04\x22\x048\x04?\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x04Kind\x07\x00\x00\x00\x04Form\x01\
+\x03\x00\x00\x00\x0c\x00N\x00l\x00i\x00g\x00h\x00\
+t\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0aMainW\
+indow\x07\x00\x00\x00\x04Form\x01\x03\
+\x00\x00\x00\x14\x04\x12\x04V\x044\x04:\x04;\x040\
+\x044\x045\x04=\x04>\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x07On hold\x07\x00\x00\x00\x04Fo\
+rm\x01\x03\x00\x00\x00\x14\x04!\x04>\x04@\x04B\
+\x04C\x042\x040\x04=\x04=\x04O\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x05Order\x07\x00\x00\x00\x04\
+Form\x01\x03\x00\x00\x00\x16\x04\x17\x040\x04?\
+\x04;\x040\x04=\x04>\x042\x040\x04=\x04>\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Planne\
+d\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\x12\
+\x04\x1f\x045\x04@\x045\x04G\x048\x04B\x04C\
+\x04N\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0aRe-r\
+eading\x07\x00\x00\x00\x04Form\x01\
+\x03\x00\x00\x00\x0a\x04'\x048\x04B\x040\x04N\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x07Reading\
+\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\x12\x04\
+\x1f\x04>\x042\x00'\x04O\x047\x040\x04=\x04\
+5\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Relat\
+ed\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\
+\x16\x04\x17\x040\x04A\x04B\x04>\x04A\x04C\x04\
+2\x040\x04B\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x05Reset\x07\x00\x00\x00\x04Form\x01\
+\x03\x00\x00\x00\x0a\x04\x1f\x04>\x04H\x04C\x04:\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x06Search\x07\
+\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\x0c\x04#\
+\x042\x04V\x049\x04B\x048\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x07Sign in\x07\x00\x00\x00\x04\
+Form\x01\x03\x00\x00\x00\x0e\x04\x06\x04A\x04B\
+\x04>\x04@\x04V\x04O\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x07History\x07\x00\x00\x00\x0aMa\
+inWindow\x01\x03\x00\x00\x00\x14\x04\x11\
+\x04V\x041\x04;\x04V\x04>\x04B\x045\x04:\
+\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Libr\
+ary\x07\x00\x00\x00\x0aMainWind\
+ow\x01\x03\x00\x00\x00\x0e\x04\x13\x04>\x04;\x04>\
+\x042\x04=\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04\
+Main\x07\x00\x00\x00\x0aMainWin\
+dow\x01\x03\x00\x00\x00\x0c\x00N\x00l\x00i\x00\
+g\x00h\x00t\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0aM\
+ainWindow\x07\x00\x00\x00\x0aMa\
+inWindow\x01\x03\x00\x00\x00\x10\x04(\
+\x048\x04:\x048\x04<\x04>\x04@\x048\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x09Shikimor\
+i\x07\x00\x00\x00\x0aMainWindow\
+\x01\x03\x00\x00\x00(\x04\x14\x04>\x044\x040\x04B\
+\x048\x00 \x044\x04>\x00 \x041\x04V\x041\
+\x04;\x04V\x04>\x04B\x045\x04:\x048\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x0eAdd to L\
+ibrary\x07\x00\x00\x00\x04Menu\x01\
+\x03\x00\x00\x00.\x04\x1e\x04G\x048\x04A\x04B\x04\
+8\x04B\x048\x00 \x04;\x04>\x04:\x040\x04\
+;\x04L\x04=\x04V\x00 \x04D\x040\x049\x04\
+;\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\x11Cle\
+ar local files\x07\x00\
+\x00\x00\x04Menu\x01\x03\x00\x00\x00*\x04\x12\x04\
+V\x044\x047\x04=\x040\x04G\x048\x04B\x04\
+8\x00 \x04?\x04@\x04>\x04G\x048\x04B\x04\
+0\x04=\x048\x04<\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x0cMark as read\x07\x00\x00\
+\x00\x04Menu\x01\x03\x00\x00\x00F\x04\x12\x04V\
+\x044\x047\x04=\x040\x04G\x048\x04B\x048\
+\x00 \x04?\x04@\x04>\x04G\x048\x04B\x040\
+\x04=\x048\x04<\x00 \x04C\x04A\x04V\x00 \
+\x04?\x04>\x04?\x045\x04@\x045\x044\x04=\
+\x04V\x08\x00\x00\x00\x00\x06\x00\x00\x00\x19Mark\
+ as read all pre\
+vious\x07\x00\x00\x00\x04Menu\x01\x03\
+\x00\x00\x00&\x04\x12\x04V\x044\x04:\x04@\x048\
+\x04B\x048\x00 \x042\x00 \x041\x04@\x040\
+\x04C\x047\x045\x04@\x04V\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x0fOpen in brow\
+ser\x07\x00\x00\x00\x04Menu\x01\x03\x00\x00\
+\x00.\x04\x12\x04V\x044\x04:\x04@\x048\x04B\
+\x048\x00 \x04;\x04>\x04:\x040\x04;\x04L\
+\x04=\x04V\x00 \x04D\x040\x049\x04;\x048\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x10Open l\
+ocal files\x07\x00\x00\x00\x04M\
+enu\x01\x03\x00\x00\x00\x18\x04\x12\x048\x044\x04\
+0\x04;\x048\x04B\x048\x00 \x042\x04A\x04\
+5\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0aRemov\
+e all\x07\x00\x00\x00\x04Menu\x01\x03\
+\x00\x00\x00*\x04\x12\x048\x044\x040\x04;\x048\
+\x04B\x048\x00 \x047\x00 \x041\x04V\x041\
+\x04;\x04V\x04>\x04B\x045\x04:\x048\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x13Remove f\
+rom library\x07\x00\x00\x00\x04\
+Menu\x01\x03\x00\x00\x00@\x04\x12\x048\x044\
+\x040\x04;\x048\x04B\x048\x00 \x042\x04V\
+\x044\x04<\x04V\x04B\x04:\x04C\x00 \x04?\
+\x04@\x04>\x00 \x04?\x04@\x04>\x04G\x048\
+\x04B\x040\x04=\x04=\x04O\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x10Remove read \
+mark\x07\x00\x00\x00\x04Menu\x01\x03\x00\
+\x00\x00&\x04\x1f\x045\x04@\x045\x042\x04V\x04\
+@\x04:\x040\x00 \x04>\x04=\x04>\x042\x04\
+;\x045\x04=\x04L\x00.\x08\x00\x00\x00\x00\x06\x00\
+\x00\x00\x12Check for upd\
+ates.\x07\x00\x00\x00\x07Messag\
+e\x01\x03\x00\x00\x006\x04\x1f\x04>\x04<\x048\x04\
+;\x04:\x040\x00 \x04?\x045\x04@\x045\x04\
+2\x04V\x04@\x04:\x048\x00 \x04>\x04=\x04\
+>\x042\x04;\x045\x04=\x04L\x00.\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00\x1bError che\
+cking for update\
+s.\x07\x00\x00\x00\x07Message\x01\x03\
+\x00\x00\x00$\x04$\x040\x049\x04;\x048\x00 \
+\x00{\x00}\x00 \x042\x048\x044\x040\x04;\
+\x045\x04=\x04V\x00.\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x1bFiles {} have \
+been removed.\x07\x00\x00\
+\x00\x07Message\x01\x03\x00\x00\x00 \x04\
+\x1c\x040\x04=\x043\x040\x00 \x00{\x00}\x00\
+ \x044\x04>\x044\x040\x04=\x04>\x00.\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x18Manga {\
+} has been added\
+.\x07\x00\x00\x00\x07Message\x01\x03\x00\
+\x00\x00$\x04\x1c\x040\x04=\x043\x040\x00 \x00\
+{\x00}\x00 \x042\x048\x044\x040\x04;\x04\
+5\x04=\x04>\x00.\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x1aManga {} has be\
+en deleted.\x07\x00\x00\x00\x07\
+Message\x01\x03\x00\x00\x00\x8c\x04\x1d\x04\
+>\x042\x040\x00 \x042\x045\x04@\x04A\x04\
+V\x04O\x00 \x00{\x00r\x00e\x00s\x00u\x00\
+l\x00t\x00}\x00 \x044\x04>\x04A\x04B\x04\
+C\x04?\x04=\x040\x00!\x00 \x04\x12\x048\x00\
+ \x042\x048\x04:\x04>\x04@\x048\x04A\x04\
+B\x04>\x042\x04C\x04T\x04B\x045\x00 \x04\
+2\x045\x04@\x04A\x04V\x04N\x00 \x00{\x00\
+A\x00P\x00P\x00_\x00V\x00E\x00R\x00S\x00\
+I\x00O\x00N\x00}\x00.\x08\x00\x00\x00\x00\x06\x00\
+\x00\x00NNew version {\
+result} is avail\
+able! You are cu\
+rrently on versi\
+on {APP_VERSION}\
+.\x07\x00\x00\x00\x07Message\x01\x03\x00\
+\x00\x00\x1e\x04\x1d\x045\x04<\x040\x04T\x00 \x04\
+7\x00'\x04T\x044\x04=\x040\x04=\x04=\x04\
+O\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0dNo co\
+nnection\x07\x00\x00\x00\x07Mes\
+sage\x01\x03\x00\x00\x00v\x04\x1d\x045\x04<\
+\x040\x04T\x00 \x044\x04>\x04A\x04B\x04C\
+\x04?\x04=\x048\x04E\x00 \x04>\x04=\x04>\
+\x042\x04;\x045\x04=\x04L\x00.\x00 \x04\x12\
+\x048\x00 \x042\x048\x04:\x04>\x04@\x048\
+\x04A\x04B\x04>\x042\x04C\x04T\x04B\x045\
+\x00 \x04>\x04A\x04B\x040\x04=\x04=\x04N\
+\x00 \x042\x045\x04@\x04A\x04V\x04N\x00.\
+\x08\x00\x00\x00\x00\x06\x00\x00\x007No upd\
+ates available. \
+You are using th\
+e latest version\
+.\x07\x00\x00\x00\x07Message\x01\x03\x00\
+\x00\x00$\x04\x1d\x04V\x04G\x04>\x043\x04>\x00\
+ \x04=\x045\x00 \x047\x04=\x040\x049\x04\
+4\x045\x04=\x04>\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x0dNothing found\x07\x00\
+\x00\x00\x07Message\x01\x03\x00\x00\x00\x0c\
+\x04\x1a\x04>\x04<\x04V\x04:\x04A\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x05Comic\x07\x00\x00\x00\x06\
+NlKind\x01\x03\x00\x00\x00\x10\x04\x14\x04>\
+\x044\x047\x048\x04=\x04A\x048\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x06Doujin\x07\x00\x00\x00\
+\x06NlKind\x01\x03\x00\x00\x00*\x04\x06\x04\
+=\x044\x04>\x04=\x045\x047\x04V\x049\x04\
+A\x04L\x04:\x048\x049\x00 \x04:\x04>\x04\
+<\x04V\x04:\x04A\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x10Indonesian comi\
+c\x07\x00\x00\x00\x06NlKind\x01\x03\x00\x00\
+\x00\x0a\x04\x1c\x040\x04=\x043\x040\x08\x00\x00\x00\
+\x00\x06\x00\x00\x00\x05Manga\x07\x00\x00\x00\x06\
+NlKind\x01\x03\x00\x00\x00\x0e\x04\x1c\x040\
+\x04=\x04L\x04E\x04C\x040\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x06Manhua\x07\x00\x00\x00\x06N\
+lKind\x01\x03\x00\x00\x00\x0c\x04\x1c\x040\x04\
+=\x04E\x042\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x06Manhwa\x07\x00\x00\x00\x06NlKi\
+nd\x01\x03\x00\x00\x00\x12\x00O\x00E\x00L\x00-\
+\x04<\x040\x04=\x043\x040\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x09OEL-manga\x07\x00\x00\
+\x00\x06NlKind\x01\x03\x00\x00\x00\x0c\x04\x12\
+\x040\x04=\x04H\x04>\x04B\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x07Oneshot\x07\x00\x00\x00\x06\
+NlKind\x01\x03\x00\x00\x00\x08\x04\x06\x04=\
+\x04H\x045\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05Ot\
+her\x07\x00\x00\x00\x06NlKind\x01\x03\
+\x00\x00\x00\x0c\x04 \x040\x04=\x04>\x041\x04M\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06Ranobe\
+\x07\x00\x00\x00\x06NlKind\x01\x03\x00\x00\x00\
+\x10\x04 \x04C\x04:\x04>\x04<\x04V\x04:\x04\
+A\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Rucom\
+ic\x07\x00\x00\x00\x06NlKind\x01\x03\x00\
+\x00\x00\x0e\x04 \x04C\x04<\x040\x04=\x043\x04\
+0\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Ruman\
+ga\x07\x00\x00\x00\x06NlKind\x01\x03\x00\
+\x00\x00\x18\x04\x1d\x045\x00 \x042\x048\x047\x04\
+=\x040\x04G\x045\x04=\x04>\x08\x00\x00\x00\x00\
+\x06\x00\x00\x00\x09Undefined\x07\x00\
+\x00\x00\x06NlKind\x01\x03\x00\x00\x00\x1e\x04\
+\x17\x040\x04E\x04V\x044\x04=\x048\x049\x00\
+ \x04:\x04>\x04<\x04V\x04:\x04A\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00\x0dWestern c\
+omic\x07\x00\x00\x00\x06NlKind\x01\
+\x03\x00\x00\x00\x14\x04\x10\x04=\x043\x04;\x04V\x04\
+9\x04A\x04L\x04:\x040\x08\x00\x00\x00\x00\x06\x00\
+\x00\x00\x07English\x07\x00\x00\x00\x0aN\
+lLanguage\x01\x03\x00\x00\x00\x10\x04\
+/\x04?\x04>\x04=\x04A\x04L\x04:\x040\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x08Japanes\
+e\x07\x00\x00\x00\x0aNlLanguage\
+\x01\x03\x00\x00\x00\x12\x04 \x04>\x04A\x04V\x049\
+\x04A\x04L\x04:\x040\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x07Russian\x07\x00\x00\x00\x0aNl\
+Language\x01\x03\x00\x00\x00\x14\x04#\
+\x04:\x04@\x040\x04W\x04=\x04A\x04L\x04:\
+\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\x09Ukra\
+inian\x07\x00\x00\x00\x0aNlLang\
+uage\x01\x03\x00\x00\x00\x18\x04\x1d\x045\x00 \
+\x042\x048\x047\x04=\x040\x04G\x045\x04=\
+\x04>\x08\x00\x00\x00\x00\x06\x00\x00\x00\x09Unde\
+fined\x07\x00\x00\x00\x0aNlLang\
+uage\x01\x03\x00\x00\x00\x08\x04\x13\x04;\x040\
+\x042\x08\x00\x00\x00\x00\x06\x00\x00\x00\x08Chap\
+ters\x07\x00\x00\x00\x05Other\x01\x03\
+\x00\x00\x00\x10\x04!\x04B\x04>\x04@\x04V\x04=\
+\x04:\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04Pa\
+ge\x07\x00\x00\x00\x05Other\x01\x03\x00\x00\
+\x00.\x04!\x04B\x04>\x04@\x04V\x04=\x04:\
+\x040\x00 \x047\x040\x042\x040\x04=\x04B\
+\x040\x046\x04C\x04T\x04B\x04L\x04A\x04O\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0fPage i\
+s loading\x07\x00\x00\x00\x05Ot\
+her\x01\x03\x00\x00\x00\x0e\x04 \x045\x049\x04\
+B\x048\x04=\x043\x08\x00\x00\x00\x00\x06\x00\x00\x00\
+\x06Rating\x07\x00\x00\x00\x05Othe\
+r\x01\x03\x00\x00\x00\x0c\x04#\x042\x04V\x049\x04\
+B\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Sig\
+n in\x07\x00\x00\x00\x05Other\x01\x03\
+\x00\x00\x00\x0c\x04!\x04B\x040\x04B\x04C\x04A\
+\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06Status\
+\x07\x00\x00\x00\x05Other\x01\x03\x00\x00\x00\x0a\
+\x04\x22\x04>\x04<\x04V\x042\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x07Volumes\x07\x00\x00\x00\x05\
+Other\x01\x03\x00\x00\x00\x14\x04\x1f\x04@\x04\
+>\x00 \x04?\x04@\x04>\x045\x04:\x04B\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x05About\x07\x00\
+\x00\x00\x11SettingsInter\
+face\x01\x03\x00\x00\x00\x1a\x04\x22\x045\x04<\
+\x040\x00 \x04?\x04@\x04>\x043\x04@\x040\
+\x04<\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\x11Ap\
+plication theme\x07\
+\x00\x00\x00\x11SettingsInte\
+rface\x01\x03\x00\x00\x00X\x04\x10\x042\x04\
+B\x04>\x04<\x040\x04B\x048\x04G\x04=\x04\
+>\x00 \x04?\x04>\x047\x04=\x040\x04G\x04\
+0\x04B\x048\x00 \x045\x04?\x04V\x047\x04\
+>\x044\x048\x00 \x04O\x04:\x00 \x04?\x04\
+5\x04@\x045\x043\x04;\x04O\x04=\x04C\x04\
+B\x04V\x08\x00\x00\x00\x00\x06\x00\x00\x00&Aut\
+omatically mark \
+episodes as watc\
+hed\x07\x00\x00\x00\x11Settings\
+Interface\x01\x03\x00\x00\x00B\x04\
+\x17\x04<\x04V\x04=\x048\x04B\x048\x00 \x04\
+7\x04>\x042\x04=\x04V\x04H\x04=\x04V\x04\
+9\x00 \x042\x048\x043\x04;\x04O\x044\x00\
+ \x04?\x04@\x04>\x043\x04@\x040\x04<\x04\
+8\x08\x00\x00\x00\x00\x06\x00\x00\x00$Chang\
+e the appearance\
+ of application\x07\
+\x00\x00\x00\x11SettingsInte\
+rface\x01\x03\x00\x00\x00B\x04\x17\x04<\x04\
+V\x04=\x040\x00 \x04@\x04>\x047\x04<\x04\
+V\x04@\x04C\x00 \x042\x04V\x044\x046\x04\
+5\x04B\x04V\x042\x00 \x04B\x040\x00 \x04\
+H\x04@\x048\x04D\x04B\x04V\x042\x08\x00\x00\
+\x00\x00\x06\x00\x00\x00$Change th\
+e size of widget\
+s and fonts\x07\x00\x00\x00\x11\
+SettingsInterfac\
+e\x01\x03\x00\x00\x00d\x04\x17\x04<\x04V\x04=\x04\
+8\x00 \x04=\x040\x041\x04C\x044\x04C\x04\
+B\x04L\x00 \x04G\x048\x04=\x04=\x04>\x04\
+A\x04B\x04V\x00 \x04?\x04V\x04A\x04;\x04\
+O\x00 \x04?\x045\x04@\x045\x047\x040\x04\
+?\x04C\x04A\x04:\x04C\x00 \x04?\x04@\x04\
+>\x043\x04@\x040\x04<\x048\x08\x00\x00\x00\x00\
+\x06\x00\x00\x009Changes wil\
+l take effect af\
+ter restarting t\
+he application\x07\x00\
+\x00\x00\x11SettingsInter\
+face\x01\x03\x00\x00\x00(\x04\x1f\x045\x04@\
+\x045\x042\x04V\x04@\x048\x04B\x048\x00 \
+\x04>\x04=\x04>\x042\x04;\x045\x04=\x04=\
+\x04O\x08\x00\x00\x00\x00\x06\x00\x00\x00\x11Chec\
+k for updates\x07\x00\x00\
+\x00\x11SettingsInterf\
+ace\x01\x03\x00\x00\x00l\x04\x1f\x045\x04@\x04\
+5\x042\x04V\x04@\x048\x04B\x048\x00 \x04\
+=\x040\x04O\x042\x04=\x04V\x04A\x04B\x04\
+L\x00 \x04>\x04=\x04>\x042\x04;\x045\x04\
+=\x04L\x00 \x04?\x04V\x044\x00 \x04G\x04\
+0\x04A\x00 \x047\x040\x04?\x04C\x04A\x04\
+:\x04C\x00 \x04?\x04@\x04>\x043\x04@\x04\
+0\x04<\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00-C\
+heck for updates\
+ when the applic\
+ation starts\x07\x00\x00\x00\
+\x11SettingsInterfa\
+ce\x01\x03\x00\x00\x00\x0a\x04\x22\x045\x04<\x04=\
+\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04Dark\
+\x07\x00\x00\x00\x11SettingsInt\
+erface\x01\x03\x00\x00\x00\x0e\x04\x15\x04?\
+\x04V\x047\x04>\x044\x048\x08\x00\x00\x00\x00\x06\
+\x00\x00\x00\x08Episodes\x07\x00\x00\x00\
+\x11SettingsInterfa\
+ce\x01\x03\x00\x00\x00\x1a\x04\x1c\x040\x04A\x04H\
+\x04B\x040\x041\x04C\x042\x040\x04=\x04=\
+\x04O\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0eInte\
+rface zoom\x07\x00\x00\x00\x11S\
+ettingsInterface\
+\x01\x03\x00\x00\x00\x08\x04\x1c\x04>\x042\x040\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00\x08Language\
+\x07\x00\x00\x00\x11SettingsInt\
+erface\x01\x03\x00\x00\x00\x0c\x04!\x042\
+\x04V\x04B\x04;\x040\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x05Light\x07\x00\x00\x00\x11Sett\
+ingsInterface\x01\x03\x00\
+\x00\x00\x1c\x04\x1f\x045\x04@\x04A\x04>\x04=\x04\
+0\x04;\x04V\x047\x040\x04F\x04V\x04O\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x0fPersona\
+lization\x07\x00\x00\x00\x11Set\
+tingsInterface\x01\x03\
+\x00\x00\x00 \x04\x1f\x04@\x04>\x045\x04:\x04B\
+\x00 \x04=\x040\x00 \x00G\x00i\x00t\x00H\
+\x00u\x00b\x08\x00\x00\x00\x00\x06\x00\x00\x00\x11Pr\
+oject on GitHub\x07\
+\x00\x00\x00\x11SettingsInte\
+rface\x01\x03\x00\x00\x00Z\x04\x12\x04A\x04\
+B\x040\x04=\x04>\x042\x04V\x04B\x04L\x00\
+ \x041\x040\x046\x040\x04=\x04C\x00 \x04\
+<\x04>\x042\x04C\x00 \x04V\x04=\x04B\x04\
+5\x04@\x04D\x045\x049\x04A\x04C\x00 \x04\
+:\x04>\x04@\x048\x04A\x04B\x04C\x042\x04\
+0\x04G\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\x22S\
+et your preferre\
+d language for U\
+I\x07\x00\x00\x00\x11SettingsIn\
+terface\x01\x03\x00\x00\x00\x12\x04\x1f\x04\
+0\x04@\x040\x04<\x045\x04B\x04@\x048\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x08Setting\
+s\x07\x00\x00\x00\x11SettingsIn\
+terface\x01\x03\x00\x00\x00\x12\x04\x1e\x04\
+=\x04>\x042\x04;\x045\x04=\x04=\x04O\x08\
+\x00\x00\x00\x00\x06\x00\x00\x00\x0fSoftwar\
+e update\x07\x00\x00\x00\x11Set\
+tingsInterface\x01\x03\
+\x00\x00\x00z\x04\x1d\x04>\x042\x040\x00 \x042\
+\x045\x04@\x04A\x04V\x04O\x00 \x041\x04C\
+\x044\x045\x00 \x041\x04V\x04;\x04L\x04H\
+\x00 \x04A\x04B\x040\x041\x04V\x04;\x04L\
+\x04=\x04>\x04N\x00 \x04V\x00 \x04<\x040\
+\x04B\x048\x04<\x045\x00 \x041\x04V\x04;\
+\x04L\x04H\x045\x00 \x04<\x04>\x046\x04;\
+\x048\x042\x04>\x04A\x04B\x045\x049\x08\x00\
+\x00\x00\x00\x06\x00\x00\x00:The new \
+version will be \
+more stable and \
+have more featur\
+es\x07\x00\x00\x00\x11SettingsI\
+nterface\x01\x03\x00\x00\x00J\x04\x12\
+\x048\x04:\x04>\x04@\x048\x04A\x04B\x04>\
+\x042\x04C\x042\x040\x04B\x048\x00 \x04A\
+\x048\x04A\x04B\x045\x04<\x04=\x04V\x00 \
+\x04=\x040\x04;\x040\x04H\x04B\x04C\x042\
+\x040\x04=\x04=\x04O\x08\x00\x00\x00\x00\x06\x00\x00\
+\x00\x12Use system set\
+ting\x07\x00\x00\x00\x11Setting\
+sInterface\x01\x03\x00\x00\x00\x14\
+\x04\x17\x040\x04<\x04>\x04@\x04>\x046\x045\
+\x04=\x04>\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06Fr\
+ozen\x07\x00\x00\x00\x06Status\x01\
+\x03\x00\x00\x00\x10\x04\x12\x048\x04E\x04>\x044\x04\
+8\x04B\x04L\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07O\
+ngoing\x07\x00\x00\x00\x06Statu\
+s\x01\x03\x00\x00\x00\x0c\x04\x12\x048\x044\x040\x04\
+=\x04>\x08\x00\x00\x00\x00\x06\x00\x00\x00\x08Rel\
+eased\x07\x00\x00\x00\x06Status\
+\x01\x88\x00\x00\x00\x0d\x11\x01\xfd)\x0b\xff\x14\x02\x04\xfd\
+,\x0a\x13\
+"
+
+qt_resource_name = b"\
+\x00\x0c\
+\x0d\xfc\x11\x13\
+\x00t\
+\x00r\x00a\x00n\x00s\x00l\x00a\x00t\x00i\x00o\x00n\x00s\
+\x00\x0d\
+\x06\x0f*\xbb\
+\x00a\
+\x00c\x00t\x00i\x00o\x00n\x00s\x00_\x00b\x00l\x00a\x00c\x00k\
+\x00\x0a\
+\x0dQ\xf6\xd3\
+\x00l\
+\x00a\x00n\x00g\x00_\x00i\x00c\x00o\x00n\x00s\
+\x00\x09\
+\x0dm\x1e%\
+\x00p\
+\x00n\x00g\x00_\x00w\x00h\x00i\x00t\x00e\
+\x00\x0d\
+\x06;\xfd\x85\
+\x00a\
+\x00c\x00t\x00i\x00o\x00n\x00s\x00_\x00w\x00h\x00i\x00t\x00e\
+\x00\x05\
+\x00o\xa6S\
+\x00i\
+\x00c\x00o\x00n\x00s\
+\x00\x07\
+\x09\xcb\xb6\x93\
+\x00b\
+\x00u\x00t\x00t\x00o\x00n\x00s\
+\x00\x0e\
+\x0f\xc9[e\
+\x00s\
+\x00v\x00g\x00_\x002\x004\x00d\x00p\x00_\x00w\x00h\x00i\x00t\x00e\
+\x00\x07\
+\x07\xab\x06\x93\
+\x00a\
+\x00c\x00t\x00i\x00o\x00n\x00s\
+\x00\x0d\
+\x05l\x1b'\
+\x00s\
+\x00h\x00i\x00k\x00i\x00m\x00o\x00r\x00i\x00.\x00s\x00v\x00g\
+\x00\x08\
+\x0aaZ\xa7\
+\x00i\
+\x00c\x00o\x00n\x00.\x00p\x00n\x00g\
+\x00\x04\
+\x00\x07(G\
+\x00l\
+\x00a\x00n\x00g\
+\x00\x06\
+\x07\x98Z\xc7\
+\x00r\
+\x00u\x00.\x00s\x00v\x00g\
+\x00\x06\
+\x06\xd5Z\xc7\
+\x00g\
+\x00b\x00.\x00s\x00v\x00g\
+\x00\x06\
+\x07\x13Z\xc7\
+\x00j\
+\x00p\x00.\x00s\x00v\x00g\
+\x00\x06\
+\x07\xb4Z\xc7\
+\x00u\
+\x00a\x00.\x00s\x00v\x00g\
+\x00\x0e\
+\x0f\xfd\x8c[\
+\x00s\
+\x00v\x00g\x00_\x002\x004\x00d\x00p\x00_\x00b\x00l\x00a\x00c\x00k\
+\x00\x04\
+\x00\x06\xc4\xee\
+\x00i\
+\x001\x008\x00n\
+\x00\x08\
+\x04Jh\xfd\
+\x00e\
+\x00n\x00_\x00U\x00S\x00.\x00q\x00m\
+\x00\x08\
+\x0bG\x8a]\
+\x00r\
+\x00u\x00_\x00R\x00U\x00.\x00q\x00m\
+\x00\x08\
+\x01IJ\xfd\
+\x00u\
+\x00k\x00_\x00U\x00A\x00.\x00q\x00m\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x1e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x17\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00p\x00\x02\x00\x00\x00\x01\x00\x00\x00\x12\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00>\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0c\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00X\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0a\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x06\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x01\x98\x00\x02\x00\x00\x00\x03\x00\x00\x00\x07\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x01\xd2\x00\x00\x00\x00\x00\x01\x00\x00ag\
+\x00\x00\x01\x912\x05\xbb\xee\
+\x00\x00\x01\xa6\x00\x00\x00\x00\x00\x01\x00\x00\x22V\
+\x00\x00\x01\x912\x05\xcd\x03\
+\x00\x00\x01\xbc\x00\x00\x00\x00\x00\x01\x00\x00AE\
+\x00\x00\x01\x912\x05\xc2\x9a\
+\x00\x00\x00\x90\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0b\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x01\x0a\x00\x00\x00\x00\x00\x01\x00\x00\x03\x8e\
+\x00\x00\x01\x89\x1a\xbc\xed\xd5\
+\x00\x00\x00\x90\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0d\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x01 \x00\x02\x00\x00\x00\x04\x00\x00\x00\x0e\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x01@\x00\x00\x00\x00\x00\x01\x00\x00\x17\xea\
+\x00\x00\x01\x89\x1a\xbc\xed\xd5\
+\x00\x00\x01R\x00\x00\x00\x00\x00\x01\x00\x00\x1c\xdb\
+\x00\x00\x01\x89\x1a\xbc\xed\xd5\
+\x00\x00\x01.\x00\x00\x00\x00\x00\x01\x00\x00\x16\xb5\
+\x00\x00\x01\x8dJ0\xc3\x91\
+\x00\x00\x01d\x00\x00\x00\x00\x00\x01\x00\x00\x1d\xd3\
+\x00\x00\x01\x8dJ0\xcb\xbc\
+\x00\x00\x00\x90\x00\x02\x00\x00\x00\x01\x00\x00\x00\x13\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x14\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\xb4\x00\x02\x00\x00\x00\x01\x00\x00\x00\x15\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\xd6\x00\x02\x00\x00\x00\x01\x00\x00\x00\x16\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\xea\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x01\x89\x1a\xbc\xed\xd1\
+\x00\x00\x00\x90\x00\x02\x00\x00\x00\x01\x00\x00\x00\x18\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\xa0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x19\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x01v\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1a\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\xd6\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1b\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\xea\x00\x00\x00\x00\x00\x01\x00\x00\x1e\xc8\
+\x00\x00\x01\x89\x1a\xbc\xed\xcd\
+"
+
+def qInitResources():
+ QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+ QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
diff --git a/data/resource.qrc b/data/resource.qrc
new file mode 100644
index 00000000..f900eb41
--- /dev/null
+++ b/data/resource.qrc
@@ -0,0 +1,22 @@
+
+
+ icons/icon.png
+
+
+ icons/lang/gb.svg
+ icons/lang/jp.svg
+ icons/lang/ru.svg
+ icons/lang/ua.svg
+
+
+ icons/buttons/svg_24dp_white/actions/shikimori.svg
+
+
+ icons/buttons/svg_24dp_black/actions/shikimori.svg
+
+
+ i18n/en_US.qm
+ i18n/ru_RU.qm
+ i18n/uk_UA.qm
+
+
diff --git a/data/translations/ru/ru.qm b/data/translations/ru/ru.qm
deleted file mode 100644
index a78465da..00000000
Binary files a/data/translations/ru/ru.qm and /dev/null differ
diff --git a/data/translations/uk/uk.qm b/data/translations/uk/uk.qm
deleted file mode 100644
index 0c95fab8..00000000
Binary files a/data/translations/uk/uk.qm and /dev/null differ
diff --git a/data/ui/dialogs/character.py b/data/ui/dialogs/character.py
index 8eb6ea70..367646cd 100644
--- a/data/ui/dialogs/character.py
+++ b/data/ui/dialogs/character.py
@@ -3,7 +3,7 @@
################################################################################
## Form generated from reading UI file 'character.ui'
##
-## Created by: Qt User Interface Compiler version 6.6.3
+## Created by: Qt User Interface Compiler version 6.7.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
@@ -40,8 +40,8 @@ def setupUi(self, Dialog):
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.image_frame.sizePolicy().hasHeightForWidth())
self.image_frame.setSizePolicy(sizePolicy)
- self.image_frame.setFrameShape(QFrame.StyledPanel)
- self.image_frame.setFrameShadow(QFrame.Raised)
+ self.image_frame.setFrameShape(QFrame.Shape.StyledPanel)
+ self.image_frame.setFrameShadow(QFrame.Shadow.Raised)
self.verticalLayout = QVBoxLayout(self.image_frame)
self.verticalLayout.setObjectName(u"verticalLayout")
self.image = QLabel(self.image_frame)
@@ -56,8 +56,8 @@ def setupUi(self, Dialog):
self.title_frame = SimpleCardWidget(Dialog)
self.title_frame.setObjectName(u"title_frame")
self.title_frame.setStyleSheet(u"")
- self.title_frame.setFrameShape(QFrame.StyledPanel)
- self.title_frame.setFrameShadow(QFrame.Raised)
+ self.title_frame.setFrameShape(QFrame.Shape.StyledPanel)
+ self.title_frame.setFrameShadow(QFrame.Shadow.Raised)
self.verticalLayout_2 = QVBoxLayout(self.title_frame)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.name_label = BodyLabel(self.title_frame)
@@ -89,7 +89,8 @@ def setupUi(self, Dialog):
self.description = TextEdit(Dialog)
self.description.setObjectName(u"description")
- self.description.setTextInteractionFlags(Qt.NoTextInteraction)
+ self.description.setFocusPolicy(Qt.FocusPolicy.NoFocus)
+ self.description.setTextInteractionFlags(Qt.TextInteractionFlag.NoTextInteraction)
self.verticalLayout_3.addWidget(self.description)
diff --git a/data/ui/dialogs/character.ui b/data/ui/dialogs/character.ui
index dcb47cc8..1a652710 100644
--- a/data/ui/dialogs/character.ui
+++ b/data/ui/dialogs/character.ui
@@ -31,10 +31,10 @@
- QFrame::StyledPanel
+ QFrame::Shape::StyledPanel
- QFrame::Raised
+ QFrame::Shadow::Raised
-
@@ -56,10 +56,10 @@
- QFrame::StyledPanel
+ QFrame::Shape::StyledPanel
- QFrame::Raised
+ QFrame::Shadow::Raised
-
@@ -85,7 +85,7 @@
-
- Qt::Vertical
+ Qt::Orientation::Vertical
@@ -115,8 +115,11 @@
-
+
+ Qt::FocusPolicy::NoFocus
+
- Qt::NoTextInteraction
+ Qt::TextInteractionFlag::NoTextInteraction
diff --git a/data/ui/widgets/info.py b/data/ui/widgets/info.py
index 9d1e4788..b01c426a 100644
--- a/data/ui/widgets/info.py
+++ b/data/ui/widgets/info.py
@@ -3,7 +3,7 @@
################################################################################
## Form generated from reading UI file 'info.ui'
##
-## Created by: Qt User Interface Compiler version 6.6.3
+## Created by: Qt User Interface Compiler version 6.7.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
@@ -38,18 +38,18 @@ def setupUi(self, Form):
self.scrollArea.setStyleSheet(u"QWidget {background: transparent;}\n"
"QScrollArea {border: none;}\n"
"")
- self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.scrollArea.setWidgetResizable(True)
self.scrollAreaWidgetContents = QWidget()
self.scrollAreaWidgetContents.setObjectName(u"scrollAreaWidgetContents")
- self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 454, 994))
+ self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 459, 990))
self.verticalLayout = QVBoxLayout(self.scrollAreaWidgetContents)
self.verticalLayout.setObjectName(u"verticalLayout")
self.verticalLayout.setContentsMargins(0, 0, -1, 0)
self.header_frame = SimpleCardWidget(self.scrollAreaWidgetContents)
self.header_frame.setObjectName(u"header_frame")
- self.header_frame.setFrameShape(QFrame.StyledPanel)
- self.header_frame.setFrameShadow(QFrame.Raised)
+ self.header_frame.setFrameShape(QFrame.Shape.StyledPanel)
+ self.header_frame.setFrameShadow(QFrame.Shadow.Raised)
self.horizontalLayout_3 = QHBoxLayout(self.header_frame)
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
self.horizontalLayout_3.setContentsMargins(9, 9, 9, 9)
@@ -61,8 +61,8 @@ def setupUi(self, Form):
self.verticalLayout_7.setObjectName(u"verticalLayout_7")
self.lib_frame = SimpleCardWidget(self.header_frame)
self.lib_frame.setObjectName(u"lib_frame")
- self.lib_frame.setFrameShape(QFrame.StyledPanel)
- self.lib_frame.setFrameShadow(QFrame.Raised)
+ self.lib_frame.setFrameShape(QFrame.Shape.StyledPanel)
+ self.lib_frame.setFrameShadow(QFrame.Shadow.Raised)
self.horizontalLayout = QHBoxLayout(self.lib_frame)
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
@@ -82,8 +82,8 @@ def setupUi(self, Form):
self.shikimori_frame = SimpleCardWidget(self.header_frame)
self.shikimori_frame.setObjectName(u"shikimori_frame")
- self.shikimori_frame.setFrameShape(QFrame.StyledPanel)
- self.shikimori_frame.setFrameShadow(QFrame.Raised)
+ self.shikimori_frame.setFrameShape(QFrame.Shape.StyledPanel)
+ self.shikimori_frame.setFrameShadow(QFrame.Shadow.Raised)
self.horizontalLayout_2 = QHBoxLayout(self.shikimori_frame)
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
@@ -177,10 +177,11 @@ def setupUi(self, Form):
self.verticalLayout_9.setObjectName(u"verticalLayout_9")
self.description_text = TextEdit(self.scrollAreaWidgetContents)
self.description_text.setObjectName(u"description_text")
- self.description_text.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
+ self.description_text.setFocusPolicy(Qt.FocusPolicy.NoFocus)
+ self.description_text.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
self.description_text.setUndoRedoEnabled(False)
self.description_text.setReadOnly(True)
- self.description_text.setTextInteractionFlags(Qt.NoTextInteraction)
+ self.description_text.setTextInteractionFlags(Qt.TextInteractionFlag.NoTextInteraction)
self.verticalLayout_9.addWidget(self.description_text)
@@ -195,7 +196,7 @@ def setupUi(self, Form):
self.characters_list = ListWidget(self.characters_frame)
self.characters_list.setObjectName(u"characters_list")
- self.characters_list.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self.characters_list.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.characters_list.setWordWrap(True)
self.verticalLayout_2.addWidget(self.characters_list)
@@ -214,7 +215,7 @@ def setupUi(self, Form):
self.related_list = ListWidget(self.related_frame)
self.related_list.setObjectName(u"related_list")
- self.related_list.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self.related_list.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.related_list.setWordWrap(True)
self.verticalLayout_4.addWidget(self.related_list)
@@ -247,7 +248,7 @@ def setupUi(self, Form):
sizePolicy2.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.items_tree.sizePolicy().hasHeightForWidth())
self.items_tree.setSizePolicy(sizePolicy2)
- self.items_tree.setContextMenuPolicy(Qt.CustomContextMenu)
+ self.items_tree.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.items_tree.header().setVisible(False)
self.verticalLayout_5.addWidget(self.items_tree)
diff --git a/data/ui/widgets/info.ui b/data/ui/widgets/info.ui
index ca63cde2..622447e8 100644
--- a/data/ui/widgets/info.ui
+++ b/data/ui/widgets/info.ui
@@ -37,7 +37,7 @@ QScrollArea {border: none;}
- Qt::ScrollBarAlwaysOff
+ Qt::ScrollBarPolicy::ScrollBarAlwaysOff
true
@@ -47,8 +47,8 @@ QScrollArea {border: none;}
0
0
- 454
- 994
+ 459
+ 990
@@ -64,10 +64,10 @@ QScrollArea {border: none;}
-
- QFrame::StyledPanel
+ QFrame::Shape::StyledPanel
- QFrame::Raised
+ QFrame::Shadow::Raised
@@ -85,7 +85,7 @@ QScrollArea {border: none;}
-
- Qt::Horizontal
+ Qt::Orientation::Horizontal
@@ -100,10 +100,10 @@ QScrollArea {border: none;}
-
- QFrame::StyledPanel
+ QFrame::Shape::StyledPanel
- QFrame::Raised
+ QFrame::Shadow::Raised
@@ -134,10 +134,10 @@ QScrollArea {border: none;}
-
- QFrame::StyledPanel
+ QFrame::Shape::StyledPanel
- QFrame::Raised
+ QFrame::Shadow::Raised
@@ -232,7 +232,7 @@ QScrollArea {border: none;}
-
- Qt::Vertical
+ Qt::Orientation::Vertical
@@ -265,8 +265,11 @@ QScrollArea {border: none;}
-
+
+ Qt::FocusPolicy::NoFocus
+
- Qt::ScrollBarAsNeeded
+ Qt::ScrollBarPolicy::ScrollBarAsNeeded
false
@@ -275,7 +278,7 @@ QScrollArea {border: none;}
true
- Qt::NoTextInteraction
+ Qt::TextInteractionFlag::NoTextInteraction
@@ -292,7 +295,7 @@ QScrollArea {border: none;}
-
- Qt::ScrollBarAlwaysOff
+ Qt::ScrollBarPolicy::ScrollBarAlwaysOff
true
@@ -315,7 +318,7 @@ QScrollArea {border: none;}
-
- Qt::ScrollBarAlwaysOff
+ Qt::ScrollBarPolicy::ScrollBarAlwaysOff
true
@@ -348,7 +351,7 @@ QScrollArea {border: none;}
- Qt::CustomContextMenu
+ Qt::ContextMenuPolicy::CustomContextMenu
false
diff --git a/main.py b/main.py
index f8b5f75c..4f93d0be 100644
--- a/main.py
+++ b/main.py
@@ -1,23 +1,27 @@
import logging
+import os
import sys
import time
from http.server import HTTPServer
-from pathlib import Path
from threading import Thread as PyThread
import darkdetect
-import platformdirs
-from PySide6.QtCore import QLocale, Qt, QThreadPool, QTranslator
+from PySide6.QtCore import QThreadPool
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QApplication
from qfluentwidgets import InfoBar, setTheme, Theme
+from data import resource
from nlightreader import ParentWindow
from nlightreader.consts.app import APP_BRANCH, APP_NAME, APP_VERSION
from nlightreader.consts.files import Icons
+from nlightreader.consts.paths import APP_DATA_PATH
from nlightreader.consts.urls import GITHUB_REPO
-from nlightreader.utils import get_html, get_locale, Thread, translate
+from nlightreader.utils.config import cfg
from nlightreader.utils.kodik_server import KodikHTTPRequestHandler
+from nlightreader.utils.threads import Thread
+from nlightreader.utils.translator import NlightTranslator, translate
+from nlightreader.utils.utils import get_html
class App(QApplication):
@@ -27,17 +31,21 @@ def __init__(self, argv):
self.setApplicationVersion(APP_VERSION)
self.setWindowIcon(QIcon(Icons.App))
- self.translator = QTranslator()
+ self.translator = NlightTranslator()
self.load_translator()
self.update_style()
def load_translator(self):
- self.translator.load(get_locale(QLocale().language()))
+ locale = cfg.get(cfg.language).value
+ self.translator.load(locale)
self.installTranslator(self.translator)
def update_style(self):
- setTheme(Theme.DARK if darkdetect.isDark() else Theme.LIGHT)
+ if (theme_mode := cfg.get(cfg.theme_mode)) == "Auto":
+ setTheme(Theme.DARK if darkdetect.isDark() else Theme.LIGHT)
+ else:
+ setTheme(Theme.DARK if theme_mode == "Dark" else Theme.LIGHT)
class MainWindow(ParentWindow):
@@ -53,22 +61,39 @@ def __init__(self):
self._update_checker = Thread(
target=self.check_for_updates,
callback=self.show_update_info,
+ error_callback=lambda: self.show_update_info(None),
)
+
+ self.settings_interface.check_for_updates_signal.connect(
+ self.start_check_for_updates_thread,
+ )
+ self.settings_interface.theme_changed.connect(
+ app.update_style,
+ )
+
self._theme_updater.start()
+ if cfg.get(cfg.check_updates_at_startup):
+ self.start_check_for_updates_thread()
+
+ def start_check_for_updates_thread(self):
+ self._update_checker.terminate()
+ self._update_checker.wait()
self._update_checker.start()
- def check_for_updates(self):
+ def check_for_updates(self) -> str | None:
response = get_html(
f"{GITHUB_REPO}/releases",
params={"per_page": 2},
content_type="json",
)
if not response:
- return
+ return None
+ latest_version = None
for release in response:
version = release["tag_name"]
if APP_BRANCH in version:
- return version
+ latest_version = version
+ return latest_version
def show_update_info(self, result):
info_bar_title = translate(
@@ -98,11 +123,21 @@ def show_update_info(self, result):
duration=info_bar_duration,
parent=self,
)
+ else:
+ InfoBar.success(
+ title=info_bar_title,
+ content=translate(
+ "Message",
+ "No updates available. You are using the latest version.",
+ ),
+ duration=info_bar_duration,
+ parent=self,
+ )
@staticmethod
def theme_listener():
theme = darkdetect.theme()
- while darkdetect.theme() == theme:
+ while darkdetect.theme() == theme or cfg.get(cfg.theme_mode) != "Auto":
time.sleep(1)
def update_style(self):
@@ -123,20 +158,19 @@ def closeEvent(self, event):
filename="latest.log",
filemode="w",
)
-
- QApplication.setHighDpiScaleFactorRoundingPolicy(
- Qt.HighDpiScaleFactorRoundingPolicy.RoundPreferFloor,
- )
- QApplication.setStyle("Fusion")
QThreadPool.globalInstance().setMaxThreadCount(32)
+
+ if cfg.get(cfg.dpi_scale) != "Auto":
+ os.environ["QT_ENABLE_HIGHDPI_SCALING"] = "0"
+ os.environ["QT_SCALE_FACTOR"] = str(cfg.get(cfg.dpi_scale))
+
app = App(sys.argv)
- Path(
- platformdirs.user_data_path() / APP_NAME,
- ).mkdir(parents=True, exist_ok=True)
+ APP_DATA_PATH.mkdir(parents=True, exist_ok=True)
- httpd = HTTPServer(("localhost", 8000), KodikHTTPRequestHandler)
- PyThread(target=httpd.serve_forever, daemon=True).start()
+ if cfg.get(cfg.enable_kodik_server):
+ httpd = HTTPServer(("localhost", 8000), KodikHTTPRequestHandler)
+ PyThread(target=httpd.serve_forever, daemon=True).start()
window = MainWindow()
window.show()
diff --git a/nlight_res.qrc b/nlight_res.qrc
deleted file mode 100644
index 0fe0901d..00000000
--- a/nlight_res.qrc
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
- data/icons/icon.png
-
-
- data/icons/lang/gb.svg
- data/icons/lang/jp.svg
- data/icons/lang/ru.svg
- data/icons/lang/ua.svg
-
-
- data/icons/buttons/svg_24dp_white/actions/shikimori.svg
-
-
- data/icons/buttons/svg_24dp_black/actions/shikimori.svg
-
-
- data/translations/uk/uk.qm
- data/translations/ru/ru.qm
-
-
diff --git a/nlight_res_rc.py b/nlight_res_rc.py
deleted file mode 100644
index 92b1bf53..00000000
--- a/nlight_res_rc.py
+++ /dev/null
@@ -1,1421 +0,0 @@
-# Resource object code (Python 3)
-# Created by: object code
-# Created by: The Resource Compiler for Qt version 6.6.3
-# WARNING! All changes made in this file will be lost!
-
-from PySide6 import QtCore
-
-qt_resource_data = b"\
-\x00\x00\x03\x8a\
-<\
-svg xmlns=\x22http:\
-//www.w3.org/200\
-0/svg\x22 width=\x2240\
-\x22 height=\x2240\x22 vi\
-ewBox=\x220 0 40 40\
-\x22>\x0d\x0a\x0d\x0a\x0d\x0a\x0d\x0a\x0d\x0a\x0d\
-\x0a\x0d\x0a\
-\x00\x00\x13#\
-\x89\
-PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
-\x00\x00\xfa\x00\x00\x00\xfa\x08\x06\x00\x00\x00\x88\xecZ=\
-\x00\x00\x12\xeaIDATx^\xed\x9di\x94U\xd5\
-\x95\x80w\xb1:\x0e\x0b\x13\x93\xb6W\x8c\xd14\x08\x88\
-\xca$\x0e\x88`\x89\x12\x09*\x0a\x8d\xb66bH\x1c\
-Iw\xd4\x12Q\x10\x11DD\x06\xc1\x01\x05Ke(\
-\xedD\x05A\xc60*j\x9cHc\xc4\x09$iM\
-kB\x96I$\x11\x89\x98\xc5\xeavE\xe8:\x1ac\
-QT\xd5\xdbw\xdfs\xa7w\xbe\xbb\x16\xbf\xdc{\x9f\
-\xbb\xbf}>OU\xbdw\xdf\xab\xd8\xbby\x9f]\xc2\
-\x05\x01\x08\x945\x81\x0aD/\xeb\xf9\xd2\x1c\x04>%\
-\x80\xe8l\x04\x08\x04@\x00\xd1\x03\x182-B\x00\xd1\
-\xd9\x03\x10\x08\x80\x00\xa2\x070dZ\x84\x00\xa2\xb3\x07\
- \x10\x00\x01D\x0f`\xc8\xb4\x08\x01Dg\x0f@ \
-\x00\x02\x88\x1e\xc0\x90i\x11\x02\x88\xce\x1e\x80@\x00\x04\
-\x10=\x80!\xd3\x22\x04\x10\x9d=\x00\x81\x00\x08 z\
-\x00C\xa6E\x08 :{\x00\x02\x01\x10@\xf4\x00\x86\
-L\x8b\x10@t\xf6\x00\x04\x02 \x80\xe8\x01\x0c\x99\x16\
-!\x80\xe8\xec\x01\x08\x04@\x00\xd1\x03\x182-B\x00\
-\xd1\xd9\x03\x10\x08\x80\x00\xa2\x070dZ\x84\x00\xa2\xb3\
-\x07 \x10\x00\x01D\x0f`\xc8\xb4\x08\x01Dg\x0f@\
- \x00\x02\x88\x1e\xc0\x90i\x11\x02\xa9\x89\xde\xafo7\
-9\xa9\xb2\x03\xc4!\x90\x1a\x81\x0f?\xdc!\xe3'>\
-\x92\xdazy^(5\xd1o\x9bDw\x0d\xbf\xfe\xca\x0c9\xbc\xed!\xb9\xec\
-\x1d\xd1\xf5cAt=\xabBE\xfa\x12\xbd{\xb7\xf6\
-\xf2\xf4\x9a)\xb9\xec\x1d\xd1\xf5cAt=\xabBE\
-\xfa\x12\xdd5}\xc3\xf5\x03e\xcc\xe8A\xb9\xeb\x1f\xd1\
-\xf5#At=\xabBE\xfa\x14\xdd5\xfe\xf8\xcaI\
-rr\x8fN\xb9b\x80\xe8\xfaq \xba\x9eU\xa1\x22\
-}\x8b~\xe8\xa1\xdf\x90_n\xac\xc9\x15\x03D\xd7\x8f\
-\x03\xd1\xf5\xac\x0a\x15\xe9[t\xd7\xfcE\x17\xf6\x96\xfb\
-\xab\x87\xe4\x86\x03\xa2\xebG\x81\xe8zV\x85\x8aLB\
-t\x07\xe0\xc1\x9aa2p@\xcf\x5c\xb0@t\xfd\x18\
-\x10]\xcf\xaaP\x91I\x89\xbe\xef\xbe{\xc9o\xde~\
-8\x17\xef\x9aCt\xfd\x96Dt=\xabBE&%\
-\xba\x83p\xfai\xc7\xc9\x92\x857g\xce\x03\xd1\xf5#\
-@t=\xabBE&)\xba\x031i\xc2\xa52t\
-\xc89\x992At=~D\xd7\xb3*Td\xd2\xa2\
-;\x18/\xad\xbbG:v843.\x88\xaeG\x8f\
-\xe8zV\x85\x8aLC\xf4\xceG\xb5\x96uk\xa7e\
-\xc6\x05\xd1\xf5\xe8\x11]\xcf\xaaP\x91i\x88\xee\x80\x0c\
-\xa9:[&O\xba,\x136\x88\xae\xc7\x8e\xe8zV\
-\x85\x8aLKt\x07e\xd1c7I\x9f3\x8eO\x9d\
-\x0f\xa2\xeb\x91#\xba\x9eU\xa1\x22\xd3\x14\xfd\xc0\x03\xbf\
-&\x9bk_rK\xfbBt=qD\xd7\xb3*T\
-d\x9a\xa2;0\x03\xce;Y~\xf4\xe0u\xa92B\
-t=nD\xd7\xb3*Td\xda\xa2;8\xf7N\xaf\
-\x92K.>=5N\x88\xaeG\x8d\xe8zV\x85\x8a\
-\xccBt\x07h\xeb\x96\x85\xd2\xbc\xf9>\xa9\xb0Bt\
-=fD\xd7\xb3*TdV\xa2\x9fT\xd9Q\xd6\xac\
-\xbe5\x15V\x88\xae\xc7\x8c\xe8zV\x85\x8a\xccJt\
-\x07\xe9\xc6Q\x83d\xd4\xc8\x81\x89\xf3Bt=bD\
-\xd7\xb3*T\xa4Ut\xf7Y\xe7\x1f\x7f\xfc\xd7\xd8\xbd\
-\xbe\xf2\xd2\xbd\xd2\xee\xc8\x16\xb1\xeb4U\x00\xd1\xf5x\
-\x11]\xcf\xaaP\x91V\xd1[\xb48P6o\xde\x12\
-\xbb\xd7\xc3\xda\x1c,\x1b_\x9b\x19\xbb\x0e\xa2\xfbA\x88\
-\xe8~8\xe6\xae\x8aU\xf4\x96-\x0f\x94)\x93\x06\xcb\
-\xbf\x0d\x1c\x1f\xbb\xa7\xcb.=C\xee\xb9\xfb\xca\xd8u\
-\x1a+\xc0\x89\xaeG\x8b\xe8zV\x85\x8a\xb4\x8a\xde\xea\
-\xd0\x83>\xfd\x96\x96s\x07\x8c\x93\xe5+^\x8c\xdd\xf3\
-\x92\x85ck\x1fk\xed\x12\xbbNC\x05\x10]\x8f\x15\
-\xd1\xf5\xac\x0a\x15i\x15\xbdu\xabo\xca\xa6\x0d\xb3>\
-\xedu\x9f\xfd\xe2\x7fy\xc3\x97\xf7\xdbW\xfe\xf4\xde\x82\
-D\xd8!\xba\x1e+\xa2\xebY\x15*\xd2*z\x9b\xd6\
-\xdf\x947^\xffLt\xf7M\xa5]\xbbW\xc5\xee\xfb\
-\xac3\xbb\xca\x82ycb\xd7\xa9_\x00\xd1\xf5H\x11\
-]\xcf\xaaP\x91V\xd1\xdb\x1ev\x88lxu\xc6\xdf\
-{\xbdv\xf8\x0c\xa9\xbe\xef'\xb1{O\xe2\xeb\x9d\x10\
-]?\x16D\xd7\xb3*T\xa4Ut\xf7\xf5K\xeek\
-\x98\xea^\x07}\xeb|\xd9\xb6\xed\xa3\xd8\xfd\xff\xf9\xfd\
-\xc5\xb2\xcf>{\xc5\xae\xf3y\x01D\xd7\xa3Dt=\
-\xabBEZE?\xe2\xf0o\xc9k/\xdf\xbf[\xaf\
-\xdb\xfe\xfc\x179\xe8\x90\x01\xb1\xfb?\xee\xd8\xb6\xf2\xc2\
-\xb3Sc\xd7A\xf4\xe8\x08\x11=:\xb3BdXE\
-worqov\xa9\x7f\xdd=}\xb1\x8c\x189;\
-v\xef\xc3\xae9W\xc6\x8f\xbb8v\x1dW\x80\x13]\
-\x8f\x11\xd1\xf5\xac\x0a\x15i\x15\xbd}\xbb\x16\xf2\xf2\xcf\
-\xf7\x14\xdd5\x7f\xec\xf1\x97\xcb\xa6_l\x8e\xcd\xc1\xfd\
-\x0d\xc0\xfd- \xee\x85\xe8z\x82\x88\xaegU\xa8H\
-\xab\xe8\x1d\xda\xb7\x94\xf5/V7\xda\xab\x8f\x97\xdc\x0e\
->\xf8\x9f\xe4\xed7\x7f\x14\x9b'\xa2\xeb\x11\x22\xba\x9e\
-U\xa1\x22\xad\xa2\xbbOuu\x9f\xee\xda\xd8\xb5x\xe9\
-Z\x19\xf8\xdd\x89\xb1Y\x5c0\xf0\xdb\xf2\xc0\xackc\
-\xd5At=>D\xd7\xb3*T\xa4U\xf4\xa3:\xb5\
-\x92\x17\x7f6\xbd\xc9^\xcf>w\xac\xacZ\xfdRl\
-\x1e\xcb\x96\xdc\x22\xdf\xe9u\x8c\xb9\xce\xec\x9aUr\xe5\
-\x90\xc6\xff\xa7\xd4T\xe1\xed\x1f,\x15\xf7\x00O(\x17\
-\xa2\x97\xe9\xa4\xad\xa2k?\xc2\xb9\xf9\xfe}\xe5\x93O\
-v\xc6\xa2\xd7\xacY\x85\xec\xd8\xbe\xdc\x5cc\xd6\xec\x95\
-Ruu\xe3\xbff \xfa\x17\x04\x10\xdd\xbc\xcd\xf2\x9d\
-h\x15\xfd\xe8\xce\xad\xe5\xbf^(\xfdY\xed/\xbf\xf2\
-+9\xb1\xc7\xd5\xb1!\xf4<\xa5\xb3\xacZ>\xc1T\
-g\xe6\xac\x15r\xd5\xd0\x86\xffpX\xaa 'z)\
-B\xfe\xfe{\xc5\xde\xcd\xfb\xec\xf2W\xae\xf1J\xb7M\
-\x1e,UW\xf4Oc\xa9\xdc\xaca\x15\xfd\xd8c\x0e\
-\x93\xb5\xcf\xdd\xa5\xeacH\xadd3je\x8b{U\
-O\xab\x92K/\x89\xfeYs3f\xae\x90!\xd7 \
-\xba\x86?'\xba\x86R\x01c\xac\xa2G}SK\x9b\
-#.\x92w\xdf\xfdSlB\x1fm[*_\xfaR\
-\xb4\xdf\x99\xef\x9b\xb1L\x86^\xbb\xfb\x9b{\xb47\xc2\
-\x89\xae%\x15?\x8e\x13=>\xc3F+\x98E?\xae\
-\xf6\xddk\xcf\xe8\xdf\xbd\xb6u\xebvqk\xc5\xbd\x8e\
-<\xe2\x9f\xe5\xd5\xf5\xf7E*s\xef\xfd\xcb\xe4\x9aa\
-\x88\xae\x81\xc6\x89\xae\xa1T\xc0\x18\xab\xe8]\xba\x1c.\
-\xcf\xff\xf4\xceH\x1d\xdf1u\x81\x8c\xba\xf1\xc1H9\
-\x0d\x05\x8f\x18>@n\xbe\xe9\xfb\xea:\xeea\x1b\xf7\
-\xd0\x8d\xe5\xe2D\xb7P\xb3\xe5p\xa2\xdb\xb8\xa9\xb2\xac\
-\xa2w\xedz\x84<\xfb\xd4\x1d\xaa5\xea\x06U\x9e2\
-T\xd6\xaf\x7f+r^\xfd\x84M\xb5\x8f\xc8\xb6\xae}\
-TVsM\xaf^\x22\xc3G|\xf6Hm\xd4\x0b\xd1\
-\xa3\x12\xb3\xc7#\xba\x9d]\xc9L\xab\xe8't=R\
-\x9ey\xea\xf6\x92\xf5\x1b\x0a\xf0\xf1\xae\xb9\xaf}u?\
-\xf9\xc3\xbb\xf3T\xebO\xbbg\x89\x5cw=\xa2k`\
-\xf1\xa3\xbb\x86R\x01c\xac\xa2w;\xa1\x9d\xfc\xf4\xc9\
-\xdbL\x1d/X\xf4\xbc\x0c\xfa~\xfc\xcft?\x7f@\
-O\xf9\xcf\x9aa%\xef!\xce\x836\x9c\xe8%\xf1z\
-\x0b\xe0D\xf7\x86r\xcfBV\xd1O\xec\xde^\x9ez\
-b\x8a\xf9\xce\xce;\xff\x16Y\xb6|\x9d9\xff\xf3D\
-\xf7\xda\xba{\x8d\xbd\xa9k\xea\xdd\x8bd\xe4\xa8\x1a\xd3\
-Z\x88n\xc2fJBt\x136]\x92U\xf4\xca\x13\
-;\xc8\x93\x8fO\xd6-\xd2H\xd4\xd7\x0f:O\xb6\x7f\
-\xb4#V\x0d\x97\xfc\xbf\x7fi\xfa5\xfa;\xefZ(\
-7\x8c~\xc0\xb4\x0e\xa2\x9b\xb0\x99\x92\x10\xdd\x84M\x97\
-d\x15\xdd\xc7W*\xfd\xfc\xa57\xa5G\xcfkt7\
-\xdaDT\xf7n\xed\xe5\xe95\x8d\xfft\x11\xe7\xaf\xfd\
-\x88\x1e{<\xea\x02\x88\xaeF\x15=\xd0*z\x8f\x93\
-:\xca\x13\xab\xe2\xff\x9e]5\xa4Zf\xd5\xac\x8c~\
-\xe3\xf52\xee\xaf\x1e\x22\x17]\xd8\xbb\xc1:\xb7\xdf\xb9\
-@F\x8f\xb1\xbd\xac\x87\xe8\xb1G\xa3.\x80\xe8jT\
-\xd1\x03\xad\xa2\x9fr\xf2Q\xb2zE\xfc\xc7P\xdd\x1d\
-w:\xfa\xdf\xe5\xad_\xbd\x1b\xfd\xe6\xebe\xec\xd8\xbe\
-L\x9a5k\xb6G\x9d)\xb7\xcf\x971cm\xcf\xb5\
-#z\xec\xb1\xa8\x0b \xba\x1aU\xf4@\xab\xe8q\x1e\
-2\xa9\x7f\x97[\xb6l\x93\x16\xad\x07E\xbf\xf9z\x19\
-\xeek\xa2\xde\xdc\xb4\xe7\xef\xe2\x93o\x9b'7\xdd\xfc\
-cS}D7a3%!\xba\x09\x9b.\xc9*\xfa\
-\xb7{v\x96\x95\xcblO\x935tgqN\xdd\xba\
-\xf5n\xb8~\xa0\x8c\x19\xbd\xfb\xff4n\x9d2O\xc6\
-\x8eCt\xcd\x8e\xe0ut\x0d\xa5\x02\xc6XE\xefu\
-\xea1\xb2|\xe9-^;>\xad\xcfHy\xf6\xb9\x0d\
-\xb1k\xfe\xf7\x1b5\xd2\xb2\xe57\xfe^g\xe2\xads\
-e\xdc\xf8\x87Mu9\xd1M\xd8LI\x9c\xe8&l\
-\xba$\xab\xe8\xee\x13_\xdc'\xbf\xf8\xbe|\xbck\xce\
-}\x22\x8c\x13\xf4\xf3k\xc2\xa4\xb9r\xcb\x04D\xd7\xcc\
-\x8a\x13]C\xa9\x801V\xd1{\x7f\xe7X\xf9\xc9\xe2\
-q\xde;~t\xfe3r\xd1%\xb6w\xdc\xd5\xbd\x99\
-\xef^p\xaa\xd4\xcc\xfc\xec\xa5\xbb\xf1\x13\xe7\xd4\xfe{\
-\xc4t\xaf\x9c\xe8&l\xa6$Nt\x136]\x92U\
-\xf4\xd3z\x1f'K\x17\xdd\xac[$b\xd4\x85\xb5\xa2\
-\xcf\xab\x15>\xee\xf5\xc4\xaaI\xd2\xe3\xa4N\xb5\xa7\xf9\
-#2a\xd2\x1cS9D7a3%!\xba\x09\x9b\
-.\xc9*\xfa\x19\xa7w\x91\xc5\x0b\xc6\xea\x161D\xb5\
-l\xf3=y\xef\xbd\x0f\x0c\x99\xbb\xa7\xb8w\xcd\xb9\xdf\
-\xcf\xdd\xef\xe9\x96\x0b\xd1-\xd4l9\x88n\xe3\xa6\xca\
-\xb2\x8a\xde\xe7\x8c\xe3e\xd1c7\xa9\xd6\xb0\x04\xad[\
-\xf7K9\xa5W\xe9\x07VJ\xd5v\xef\xe0\xab<\xb1\
-\xbdL\x9a\xfch\xa9\xd0\x06\xff;\xa2\x9b\xb0\x99\x92\x10\
-\xdd\x84M\x97d\x15\xfd\xcc>]e\xe1|\xff_q\
-\x5c\xf7\xae\xaf\xbf\xa1F\xee\x9a\xb6H\xd7H\x13Q\xee\
-^W\xac|\xd1T\x07\xd1M\xd8LI\x88n\xc2\xa6\
-K\xb2\x8a\xde\xf7\xac\x13\xe4\xb1Go\xd4-\x12#\xaa\
-[\xe5U\xf2\xeako\xc7\xa8\x10/\x15\xd1\xe3\xf1\x8b\
-\x92\x8d\xe8QhE\x8c\xb5\x8a\xde\xafo7\x99?w\
-t\xc4\xd5\xa2\x87\xff\xfe\xf7[\xa5U[\xfd\xc7FE\
-_\xa1\xe9\x0cD\xf7M\xb4\xf1z\x88\x9e k\xab\xe8\
-\xff\xd2\xaf\x9b\xcc\x9b\x93\xbc\xe8\xaeu\xf7\xe3\xbb\xfb1\
->\x8b\x0b\xd1\xd3\xa3\x8e\xe8\x09\xb2\xb6\x8a\xde\xbf_w\
-yt\xce\xa8\x04\xefl\xf7\xd2\xbe\xbe\xde)\xea\x0d#\
-zTb\xf6xD\xb7\xb3+\x99i\x15\xfd\x9c\xfe\x95\
-2\xe7\xe1\x91%\xeb\xfb\x0c\xf8\xc7\xaf\x9f#;v\xfc\
-\x9f\xcf\x92%k!zID\xde\x02\x10\xdd\x1b\xca=\
-\x0b\x15I\xf4y\x8f=+\x17^l\xff\xf8*\x0bF\
-D\xb7P\xb3\xe5 \xba\x8d\x9b*\xabH\xa2\xbb\x86.\
-\xaf\x9a.\x0f<\xb8Z\xd5\x9b\x8f D\xf7AQW\
-\x03\xd1u\x9cLQE\x13\xdd5\xd9\xae\xe3e\xf2\xce\
-\xaf\xff`\xea7j\x12\xa2G%f\x8fGt;\xbb\
-\x92\x99E\x14\xfd\x85\xb5oH\xaf\xd3F\x94\xec\xcdG\
-\x00\xa2\xfb\xa0\xa8\xab\x81\xe8:N\xa6\xa8\x22\x8a\xee\x1a\
-\x1dW\xfb\xd8\xe9\xc4\xda\xc7O\x93\xbe\x10=i\xc2_\
-\xd4G\xf4\x04Y\x17Ut\x87\xe4\xd4\xde\xd7\xc9\xda\x9f\
-mJ\x90\x8e|\xfa\x5c\xbb{\xbe=\x94\x8b\xe7\xd1\xcb\
-t\xd2E\x16\xfd\xb7\xbf\xfd\xa3\xb4mwq\xa2\x93A\
-\xf4D\xf1\xeeV\x9c\x13=A\xd6E\x16\xdda\x995\
-{\xa5T]]\x9d\x18!DO\x0c\xed\x1e\x85\x11=\
-A\xd6E\x17\xdd\xa1q\xdf\xe3\xe6\xbe\xcf-\x89\x0b\xd1\
-\x93\xa0\xdapMDO\x90u9\x88\xee\xf0\x1c\xd2\xf2\
-\x02y\xff\xfd\x0f\xbd\x93Bt\xefH\x1b-\x88\xe8\x09\
-\xb2.\x17\xd1\xdd\x176\xba/n\xf4}!\xbao\xa2\
-\x8d\xd7C\xf4\x04Y\x97\x8b\xe8\x0e\xd1\xb0\xebf\xca=\
-\xf7~\xf1\xe9\xaf>\xb0!\xba\x0f\x8a\xba\x1a\x88\xae\xe3\
-d\x8a*'\xd1\x1d\x80.'\x5c)\x1b\xdf\xf8\xb5\x89\
-ECI\x88\xee\x0de\xc9B\x88^\x12\x91=\xa0\xdc\
-D_\xff\xf2[Ry\xf2P;\x90z\x99\x88\xee\x0d\
-e\xc9B\x88^\x12\x91=\xa0\xdcDw$\xe2|{\
-j}\x92\x88n\xdf[Q3\x11=*\xb1\x08\xf1\xe5\
-(\xbak\xbfo\xff\x1be\xcd\x93\xafD \xd1p(\
-\xa2\xc7F\xa8.\x80\xe8jT\xd1\x03\xcbU\xf4\xdf\xfd\
-\xee}9\xb2\xe3\xa5\xf2\xf1\xc7\x7f\x8d\x0e\xa5N\x06\xa2\
-\xc7\xc2\x17)\x19\xd1#\xe1\x8a\x16\x5c\xae\xa2;\x0as\
-\xe6>-\x97\x0c\xbe#\x1a\x10~G\x97\xe9\xd5~_\
-\xb9\xd0\x0e\x00\xd1\xb5\xa4\x0cq\xe5,\xba\xc3\xf1\x83\x1f\
-\xde%?~h\x8d\x81\xccg)\x9c\xe8ft\x91\x13\
-\x11=22}B\xb9\x8b\xfe\xc9';\xa5]\xa7\xcb\
-d\xf3\xe6-z(\xfc\xe8nb\x157\x09\xd1\xe3\x12\
-l\x22\x7f\xd7\xae]\xe6\xea\x15\x15\x15\xe6\xdc4\x13C\
-\xe8\xd1\x17O\x1eS\xf5E\x92:\x10\xc81\x01D\xcf\
-\xf1p\xb85\x08\xf8\x22\x80\xe8\xbeHR\x07\x029&\
-\x80\xe89\x1e\x0e\xb7\x06\x01_\x04\x10\xdd\x17I\xea@\
- \xc7\x04\x10=\xc7\xc3\xe1\xd6 \xe0\x8b\x00\xa2\xfb\x22\
-I\x1d\x08\xe4\x98\x00\xa2\xe7x8\xdc\x1a\x04|\x11@\
-t_$\xa9\x03\x81\x1c\x13@\xf4\x1c\x0f\x87[\x83\x80\
-/\x02\x88\xee\x8b$u \x90c\x02\x88\x9e\xe3\xe1p\
-k\x10\xf0E\x00\xd1}\x91\xa4\x0e\x04rL\x00\xd1s\
-<\x1cn\x0d\x02\xbe\x08 \xba/\x92\xd4\x81@\x8e\x09\
- z\x8e\x87\xc3\xadA\xc0\x17\x01D\xf7E\x92:\x10\
-\xc81\x81 D\xff\xea\xfe\xcd\xe5\xf1U\xb7\xcaQ\x9d\
-Z\xe5x\x14\xdc\x1a\x04\x92#\x10\x84\xe8\x0e\x1f\xb2'\
-\xb7\x89\xa8\x9c\x7f\x02\xc1\x88\x8e\xec\xf9\xdf\x8c\xdcar\
-\x04\x82\x12\x1d\xd9\x93\xdbHT\xce7\x81\xe0DG\xf6\
-|oH\xee.\x19\x02A\x8a\x8e\xec\xc9l&\xaa\xe6\
-\x97@\xb0\xa2#{~7%w\xe6\x9f@\xd0\xa2#\
-\xbb\xff\x0dE\xc5|\x12\x08^td\xcf\xe7\xc6\xe4\xae\
-\xfc\x12@\xf4\xbf\xf1\xe4uv\xbf\x1b\x8bj\xf9\x22\x80\
-\xe8u\xe6\x81\xec\xf9\xda\x9c\xdc\x8d?\x02\x88^\x8f%\
-\xb2\xfb\xdb\x5cT\xca\x0f\x01Do`\x16\xc8\x9e\x9f\x0d\
-\xca\x9d\xf8!\x80\xe8\x8dpDv?\x1b\x8c*\xf9 \
-\x80\xe8M\xcc\x01\xd9\xf3\xb1I\xb9\x8b\xf8\x04\x10\xbd\x04\
-Cd\x8f\xbf\xc9\xa8\x90=\x01DW\xcc\x00\xd9\x15\x90\
-\x08\xc95\x01DW\x8e\x07\xd9\x95\xa0\x08\xcb%\x01D\
-\x8f0\x16d\x8f\x00\x8b\xd0\x5c\x11@\xf4\x88\xe3@\xf6\
-\x88\xc0\x08\xcf\x05\x01D7\x8c\x01\xd9\x0d\xd0H\xc9\x94\
-\x00\xa2\x1b\xf1#\xbb\x11\x1ci\x99\x10@\xf4\x18\xd8\x91\
-=\x06\
-\
-\
-\x00\x00\x04\xed\
-<\
-svg xmlns=\x22http:\
-//www.w3.org/200\
-0/svg\x22 viewBox=\x22\
-0 0 512 512\x22 sty\
-le=\x22enable-backg\
-round:new 0 0 51\
-2 512\x22 xml:space\
-=\x22preserve\x22><\
-path style=\x22fill\
-:#d80027\x22 d=\x22M20\
-0.348 196.634 0 \
-85.33v31.474l143\
-.693 79.83z\x22/>\
-\x00\x00\x00\xf4\
-<\
-svg xmlns=\x22http:\
-//www.w3.org/200\
-0/svg\x22 viewBox=\x22\
-0 0 512 512\x22 sty\
-le=\x22enable-backg\
-round:new 0 0 51\
-2 512\x22 xml:space\
-=\x22preserve\x22>\
-\x00\x00\x00\xf1\
-<\
-svg xmlns=\x22http:\
-//www.w3.org/200\
-0/svg\x22 viewBox=\x22\
-0 0 512 512\x22 sty\
-le=\x22enable-backg\
-round:new 0 0 51\
-2 512\x22 xml:space\
-=\x22preserve\x22>\
-\
-\x00\x00\x03\x8a\
-<\
-svg xmlns=\x22http:\
-//www.w3.org/200\
-0/svg\x22 width=\x2240\
-\x22 height=\x2240\x22 vi\
-ewBox=\x220 0 40 40\
-\x22>\x0d\x0a\x0d\x0a\x0d\x0a\x0d\x0a\x0d\x0a\x0d\
-\x0a\x0d\x0a\
-\x00\x00\x14\xe5\
-<\
-\xb8d\x18\xca\xef\x9c\x95\xcd!\x1c\xbf`\xa1\xbd\xdd\xa7\
-\x00\x00\x00\x05ru_RUB\x00\x00\x02h\x00\x05\
- D\x00\x00\x03\xe8\x00\x050\xa4\x00\x00\x01&\x00\x05\
-2\xde\x00\x00\x0a\xee\x00\x057\xfe\x00\x00\x063\x00\x05\
-g\xd5\x00\x00\x10\xb8\x00 \x7fy\x00\x00\x06\x99\x00 \
-\xfdw\x00\x00\x10\xe6\x00Hw9\x00\x00\x02\x84\x00J\
-c\xf3\x00\x00\x0c\x92\x00S]\xfe\x00\x00\x01Q\x00S\
-\x84\xd1\x00\x00\x0dB\x00V\x8a\xc2\x00\x00\x04j\x00V\
-\xae\xc2\x00\x00\x0e,\x00X\xc9\xc4\x00\x00\x05m\x00\xaa\
-\xb4\x93\x00\x00\x01\xd9\x00\xbf\xf1\xf4\x00\x00\x00x\x01'\
-\xda.\x00\x00\x0b\xa0\x01\xa5\xcc{\x00\x00\x09s\x02\xf9\
-\x88\xc9\x00\x00\x05\xf9\x03{\x0a~\x00\x00\x0a(\x03\xdb\
-sg\x00\x00\x04\x0b\x03\xdbsg\x00\x00\x06d\x04r\
-\x80\xd4\x00\x00\x03\x1a\x04r\x80\xd4\x00\x00\x11\xe7\x04\x98\
-I\xbc\x00\x00\x00K\x04\xac,\xa5\x00\x00\x00\xbc\x04\xb6\
-\xc0\xfe\x00\x00\x0c\xbe\x04\xc0RN\x00\x00\x09\xd2\x058\
-O\xb1\x00\x00\x0dl\x058O\xd1\x00\x00\x0d\x9b\x05\x88\
-U\x85\x00\x00\x0eX\x05\x88\xb0G\x00\x00\x01\xaa\x05\x88\
-\xb0G\x00\x00\x117\x05\x90_\xc2\x00\x00\x08A\x05\xaa\
-\x8b\xc3\x00\x00\x11\x90\x05\xb5\x1b\xbe\x00\x00\x0bH\x05\xc6\
-\xa8\xa5\x00\x00\x02S\x06\x06\xf6t\x00\x00\x04:\x06!\
-\xd8#\x00\x00\x0c\xef\x06L\x9f4\x00\x00\x0d\xfe\x06N\
-`\x17\x00\x00\x12\x17\x06\xc2\xecl\x00\x00\x08\xe4\x07(\
-T\xe4\x00\x00\x04\x9c\x08^\x80\x03\x00\x00\x07\xc9\x08h\
-I\x95\x00\x00\x0f\xa5\x08~2\x13\x00\x00\x02\xe5\x08\x80\
-\x13^\x00\x00\x10\x10\x08\x9e=\xd9\x00\x00\x06\xd1\x08\xa8\
-1s\x00\x00\x02\xb4\x08\xaa\xe3\xe4\x00\x00\x01{\x08\xb1\
-D~\x00\x00\x0a\x95\x08\xb2(\x07\x00\x00\x04\xd8\x08\xb7\
-\xb0\x17\x00\x00\x05\x11\x08\xc2\x8a\xe4\x00\x00\x05;\x09\x9a\
-\xc0\x05\x00\x00\x00\x00\x09\xbac\xa3\x00\x00\x0e\x85\x09\xc3\
-\x84\x81\x00\x00\x0e\xb7\x09\xca\x9f.\x00\x00\x0f\xdc\x09\xfe\
-\x06\xae\x00\x00\x02'\x09\xfe\x06\xae\x00\x00\x05\x9b\x09\xfe\
-\x06\xae\x00\x00\x11e\x0aU\x903\x00\x00\x07\x22\x0a\xbc\
-\xb8\xf4\x00\x00\x0e\xe7\x0a\xbc\xb8\xf4\x00\x00\x10L\x0b\x96\
-v\xf4\x00\x00\x03N\x0c\x1c\xf5$\x00\x00\x03\xaa\x0cN\
-0\xd8\x00\x00\x0fk\x0cf\xa1u\x00\x00\x00\xeb\x0c\xe4\
-\xbb\xc9\x00\x00\x09\x1d\x0d\x03\xac\xd3\x00\x00\x03|\x0dc\
-\xc3\x93\x00\x00\x11\xbc\x0do\x06\x14\x00\x00\x07|\x0d\xa5\
-\x0f\x13\x00\x00\x0f%\x0e\x14\xdds\x00\x00\x08\x8d\x0e\x87\
-\xa8\x03\x00\x00\x10\x8e\x0f\x0a\xb6\xd9\x00\x00\x05\xc5\x0fC\
-7A\x00\x00\x0d\xc8i\x00\x00\x12G\x03\x00\x00\x00\x1e\
-\x04\x1a\x04>\x044\x00 \x040\x042\x04B\x04>\
-\x04@\x048\x047\x040\x04F\x048\x048\x08\x00\
-\x00\x00\x00\x06\x00\x00\x00\x12Authoriz\
-ation code\x07\x00\x00\x00\x06D\
-ialog\x01\x03\x00\x00\x00\x0c\x04\x1e\x04B\x04\
-<\x045\x04=\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\
-\x06Cancel\x07\x00\x00\x00\x06Dial\
-og\x01\x03\x00\x00\x00\x1c\x04\x1f\x04@\x04>\x04G\
-\x048\x04B\x040\x04=\x04>\x00 \x043\x04;\
-\x040\x042\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0dCh\
-apters read\x07\x00\x00\x00\x06\
-Dialog\x01\x03\x00\x00\x00\x0e\x04#\x044\
-\x040\x04;\x048\x04B\x04L\x08\x00\x00\x00\x00\x06\
-\x00\x00\x00\x06Delete\x07\x00\x00\x00\x06D\
-ialog\x01\x03\x00\x00\x00\x18\x04\x1f\x04>\x04\
-;\x04C\x04G\x048\x04B\x04L\x00 \x04:\x04\
->\x044\x08\x00\x00\x00\x00\x06\x00\x00\x00\x08Get\
- code\x07\x00\x00\x00\x06Dialog\
-\x01\x03\x00\x00\x00\x0c\x04!\x04?\x048\x04A\x04>\
-\x04:\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04List\
-\x07\x00\x00\x00\x06Dialog\x01\x03\x00\x00\x00\
-\x0a\x04\x1b\x04>\x043\x048\x04=\x08\x00\x00\x00\x00\
-\x06\x00\x00\x00\x05Login\x07\x00\x00\x00\x06D\
-ialog\x01\x03\x00\x00\x00\x0c\x04\x1f\x040\x04\
-@\x04>\x04;\x04L\x08\x00\x00\x00\x00\x06\x00\x00\x00\
-\x08Password\x07\x00\x00\x00\x06Di\
-alog\x01\x03\x00\x00\x00\x0e\x04 \x045\x049\
-\x04B\x048\x04=\x043\x08\x00\x00\x00\x00\x06\x00\x00\
-\x00\x06Rating\x07\x00\x00\x00\x06Dia\
-log\x01\x03\x00\x00\x00&\x04\x1f\x04>\x04:\x04\
-0\x047\x04K\x042\x040\x04B\x04L\x00 \x04\
-A\x04?\x04>\x049\x04;\x045\x04@\x04K\x08\
-\x00\x00\x00\x00\x06\x00\x00\x00\x0dShow sp\
-oilers\x07\x00\x00\x00\x06Dialo\
-g\x01\x03\x00\x00\x00\x0a\x04\x12\x04>\x049\x04B\x04\
-8\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Sign \
-in\x07\x00\x00\x00\x06Dialog\x01\x03\x00\
-\x00\x00\x10\x04\x1e\x041\x04=\x04>\x042\x048\x04\
-B\x04L\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06Upd\
-ate\x07\x00\x00\x00\x06Dialog\x01\x03\
-\x00\x00\x00\x12\x04\x1f\x04@\x048\x04<\x045\x04=\
-\x048\x04B\x04L\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05\
-Apply\x07\x00\x00\x00\x04Form\x01\x03\
-\x00\x00\x00\x10\x04\x1a\x040\x04B\x040\x04;\x04>\
-\x043\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\x08Ca\
-talogs\x07\x00\x00\x00\x04Form\x01\
-\x03\x00\x00\x00\x12\x04\x1f\x045\x04@\x04A\x04>\x04\
-=\x040\x046\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\
-\x0aCharacters\x07\x00\x00\x00\x04\
-Form\x01\x03\x00\x00\x00\x12\x04\x1f\x04@\x04>\
-\x04G\x048\x04B\x040\x04=\x04>\x08\x00\x00\x00\
-\x00\x06\x00\x00\x00\x09Completed\x07\
-\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\x0e\x04\x11\
-\x04@\x04>\x04H\x045\x04=\x04>\x08\x00\x00\x00\
-\x00\x06\x00\x00\x00\x07Dropped\x07\x00\x00\
-\x00\x04Form\x01\x03\x00\x00\x00\x0e\x04$\x048\
-\x04;\x04L\x04B\x04@\x04K\x08\x00\x00\x00\x00\x06\
-\x00\x00\x00\x07Filters\x07\x00\x00\x00\x04\
-Form\x01\x03\x00\x00\x00\x1a\x04!\x04?\x048\
-\x04A\x04>\x04:\x00 \x046\x040\x04=\x04@\
-\x04>\x042\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0bGe\
-nres list\x07\x00\x00\x00\x04Fo\
-rm\x01\x03\x00\x00\x00\x06\x04\x22\x048\x04?\x08\x00\
-\x00\x00\x00\x06\x00\x00\x00\x04Kind\x07\x00\x00\x00\
-\x04Form\x01\x03\x00\x00\x00\x0c\x00N\x00l\x00\
-i\x00g\x00h\x00t\x08\x00\x00\x00\x00\x06\x00\x00\x00\
-\x0aMainWindow\x07\x00\x00\x00\x04\
-Form\x01\x03\x00\x00\x00\x10\x04\x1e\x04B\x04;\
-\x04>\x046\x045\x04=\x04>\x08\x00\x00\x00\x00\x06\
-\x00\x00\x00\x07On hold\x07\x00\x00\x00\x04\
-Form\x01\x03\x00\x00\x00\x14\x04!\x04>\x04@\
-\x04B\x048\x04@\x04>\x042\x04:\x040\x08\x00\
-\x00\x00\x00\x06\x00\x00\x00\x05Order\x07\x00\x00\
-\x00\x04Form\x01\x03\x00\x00\x00\x1c\x04\x17\x040\
-\x04?\x04;\x040\x04=\x048\x04@\x04>\x042\
-\x040\x04=\x04=\x04>\x08\x00\x00\x00\x00\x06\x00\x00\
-\x00\x07Planned\x07\x00\x00\x00\x04Fo\
-rm\x01\x03\x00\x00\x00\x16\x04\x1f\x045\x04@\x045\
-\x04G\x048\x04B\x04K\x042\x040\x04N\x08\x00\
-\x00\x00\x00\x06\x00\x00\x00\x0aRe-readi\
-ng\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\
-\x0a\x04'\x048\x04B\x040\x04N\x08\x00\x00\x00\x00\
-\x06\x00\x00\x00\x07Reading\x07\x00\x00\x00\
-\x04Form\x01\x03\x00\x00\x00\x12\x04!\x042\x04\
-O\x047\x040\x04=\x04=\x04>\x045\x08\x00\x00\
-\x00\x00\x06\x00\x00\x00\x07Related\x07\x00\
-\x00\x00\x04Form\x01\x03\x00\x00\x00\x10\x04!\x04\
-1\x04@\x04>\x04A\x048\x04B\x04L\x08\x00\x00\
-\x00\x00\x06\x00\x00\x00\x05Reset\x07\x00\x00\x00\
-\x04Form\x01\x03\x00\x00\x00\x0a\x04\x12\x04>\x04\
-9\x04B\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07S\
-ign in\x07\x00\x00\x00\x04Form\x01\
-\x03\x00\x00\x00\x0e\x04\x18\x04A\x04B\x04>\x04@\x04\
-8\x04O\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07His\
-tory\x07\x00\x00\x00\x0aMainWin\
-dow\x01\x03\x00\x00\x00\x14\x04\x11\x048\x041\x04\
-;\x048\x04>\x04B\x045\x04:\x040\x08\x00\x00\
-\x00\x00\x06\x00\x00\x00\x07Library\x07\x00\
-\x00\x00\x0aMainWindow\x01\x03\x00\
-\x00\x00\x0e\x04\x13\x04;\x040\x042\x04=\x040\x04\
-O\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04Main\x07\
-\x00\x00\x00\x0aMainWindow\x01\x03\
-\x00\x00\x00\x0c\x00N\x00l\x00i\x00g\x00h\x00t\
-\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0aMainWi\
-ndow\x07\x00\x00\x00\x0aMainWin\
-dow\x01\x03\x00\x00\x00\x10\x04(\x048\x04:\x04\
-8\x04<\x04>\x04@\x048\x08\x00\x00\x00\x00\x06\x00\
-\x00\x00\x09Shikimori\x07\x00\x00\x00\
-\x0aMainWindow\x01\x03\x00\x00\x00\
-*\x04\x14\x04>\x041\x040\x042\x048\x04B\x04\
-L\x00 \x042\x00 \x041\x048\x041\x04;\x04\
-8\x04>\x04B\x045\x04:\x04C\x08\x00\x00\x00\x00\
-\x06\x00\x00\x00\x0eAdd to Libr\
-ary\x07\x00\x00\x00\x04Menu\x01\x03\x00\x00\
-\x000\x04\x1e\x04G\x048\x04A\x04B\x048\x04B\
-\x04L\x00 \x04;\x04>\x04:\x040\x04;\x04L\
-\x04=\x04K\x045\x00 \x04D\x040\x049\x04;\
-\x04K\x08\x00\x00\x00\x00\x06\x00\x00\x00\x11Clea\
-r local files\x07\x00\x00\
-\x00\x04Menu\x01\x03\x00\x00\x00(\x04\x1e\x04B\
-\x04<\x045\x04B\x048\x04B\x04L\x00 \x04?\
-\x04@\x04>\x04G\x048\x04B\x040\x04=\x04=\
-\x04K\x04<\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0cMa\
-rk as read\x07\x00\x00\x00\x04M\
-enu\x01\x03\x00\x00\x00F\x04\x1e\x04B\x04<\x04\
-5\x04B\x048\x04B\x04L\x00 \x04?\x04@\x04\
->\x04G\x048\x04B\x040\x04=\x04=\x04K\x04\
-<\x00 \x042\x04A\x045\x00 \x04?\x04@\x04\
-5\x044\x04K\x044\x04C\x04I\x048\x045\x08\
-\x00\x00\x00\x00\x06\x00\x00\x00\x19Mark as\
- read all previo\
-us\x07\x00\x00\x00\x04Menu\x01\x03\x00\x00\x00\
-$\x04\x1e\x04B\x04:\x04@\x04K\x04B\x04L\x00\
- \x042\x00 \x041\x04@\x040\x04C\x047\x04\
-5\x04@\x045\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0fO\
-pen in browser\x07\x00\
-\x00\x00\x04Menu\x01\x03\x00\x00\x00.\x04\x1e\x04\
-B\x04:\x04@\x04K\x04B\x04L\x00 \x04;\x04\
->\x04:\x040\x04;\x04L\x04=\x04K\x045\x00\
- \x04D\x040\x049\x04;\x04K\x08\x00\x00\x00\x00\
-\x06\x00\x00\x00\x10Open local \
-files\x07\x00\x00\x00\x04Menu\x01\x03\
-\x00\x00\x00\x16\x04#\x044\x040\x04;\x048\x04B\
-\x04L\x00 \x042\x04A\x045\x08\x00\x00\x00\x00\x06\
-\x00\x00\x00\x0aRemove all\x07\x00\
-\x00\x00\x04Menu\x01\x03\x00\x00\x00*\x04#\x04\
-4\x040\x04;\x048\x04B\x04L\x00 \x048\x04\
-7\x00 \x041\x048\x041\x04;\x048\x04>\x04\
-B\x045\x04:\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\
-\x13Remove from lib\
-rary\x07\x00\x00\x00\x04Menu\x01\x03\x00\
-\x00\x006\x04#\x044\x040\x04;\x048\x04B\x04\
-L\x00 \x04>\x04B\x04<\x045\x04B\x04:\x04\
-C\x00 \x04>\x00 \x04?\x04@\x04>\x04G\x04\
-B\x045\x04=\x048\x048\x08\x00\x00\x00\x00\x06\x00\
-\x00\x00\x10Remove read m\
-ark\x07\x00\x00\x00\x04Menu\x01\x03\x00\x00\
-\x00(\x04\x1f\x04@\x04>\x042\x045\x04@\x04:\
-\x040\x00 \x04>\x041\x04=\x04>\x042\x04;\
-\x045\x04=\x048\x049\x00.\x08\x00\x00\x00\x00\x06\
-\x00\x00\x00\x12Check for up\
-dates.\x07\x00\x00\x00\x07Messa\
-ge\x01\x03\x00\x00\x006\x04\x1e\x04H\x048\x041\
-\x04:\x040\x00 \x04?\x04@\x04>\x042\x045\
-\x04@\x04:\x048\x00 \x04>\x041\x04=\x04>\
-\x042\x04;\x045\x04=\x048\x049\x00.\x08\x00\
-\x00\x00\x00\x06\x00\x00\x00\x1bError ch\
-ecking for updat\
-es.\x07\x00\x00\x00\x07Message\x01\
-\x03\x00\x00\x00\x22\x04$\x040\x049\x04;\x04K\x00\
- \x00{\x00}\x00 \x04C\x044\x040\x04;\x04\
-5\x04=\x04K\x00.\x08\x00\x00\x00\x00\x06\x00\x00\x00\
-\x1bFiles {} have b\
-een removed.\x07\x00\x00\x00\
-\x07Message\x01\x03\x00\x00\x00&\x04\x1c\
-\x040\x04=\x043\x040\x00 \x00{\x00}\x00 \
-\x044\x04>\x041\x040\x042\x04;\x045\x04=\
-\x040\x00.\x08\x00\x00\x00\x00\x06\x00\x00\x00\x18Ma\
-nga {} has been \
-added.\x07\x00\x00\x00\x07Messa\
-ge\x01\x03\x00\x00\x00\x22\x04\x1c\x040\x04=\x043\
-\x040\x00 \x00{\x00}\x00 \x04C\x044\x040\
-\x04;\x045\x04=\x040\x00.\x08\x00\x00\x00\x00\x06\
-\x00\x00\x00\x1aManga {} has\
- been deleted.\x07\x00\
-\x00\x00\x07Message\x01\x03\x00\x00\x00\x88\
-\x04\x1d\x04>\x042\x040\x04O\x00 \x042\x045\
-\x04@\x04A\x048\x04O\x00 \x00{\x00r\x00e\
-\x00s\x00u\x00l\x00t\x00}\x00 \x044\x04>\
-\x04A\x04B\x04C\x04?\x04=\x040\x00!\x00 \
-\x04\x12\x04K\x00 \x048\x04A\x04?\x04>\x04;\
-\x04L\x047\x04C\x045\x04B\x045\x00 \x042\
-\x045\x04@\x04A\x048\x04N\x00 \x00{\x00A\
-\x00P\x00P\x00_\x00V\x00E\x00R\x00S\x00I\
-\x00O\x00N\x00}\x00.\x08\x00\x00\x00\x00\x06\x00\x00\
-\x00NNew version {r\
-esult} is availa\
-ble! You are cur\
-rently on versio\
-n {APP_VERSION}.\
-\x07\x00\x00\x00\x07Message\x01\x03\x00\x00\
-\x00\x0c\x04\x1a\x04>\x04<\x048\x04:\x04A\x08\x00\
-\x00\x00\x00\x06\x00\x00\x00\x05Comic\x07\x00\x00\
-\x00\x06NlKind\x01\x03\x00\x00\x00\x10\x04\x14\
-\x04>\x044\x047\x048\x04=\x04A\x048\x08\x00\
-\x00\x00\x00\x06\x00\x00\x00\x06Doujin\x07\x00\
-\x00\x00\x06NlKind\x01\x03\x00\x00\x00(\x04\
-\x18\x04=\x044\x04>\x04=\x045\x047\x048\x04\
-9\x04A\x04:\x048\x049\x00 \x04:\x04>\x04\
-<\x048\x04:\x04A\x08\x00\x00\x00\x00\x06\x00\x00\x00\
-\x10Indonesian comi\
-c\x07\x00\x00\x00\x06NlKind\x01\x03\x00\x00\
-\x00\x0a\x04\x1c\x040\x04=\x043\x040\x08\x00\x00\x00\
-\x00\x06\x00\x00\x00\x05Manga\x07\x00\x00\x00\x06\
-NlKind\x01\x03\x00\x00\x00\x0e\x04\x1c\x040\
-\x04=\x04L\x04E\x04C\x040\x08\x00\x00\x00\x00\x06\
-\x00\x00\x00\x06Manhua\x07\x00\x00\x00\x06N\
-lKind\x01\x03\x00\x00\x00\x0c\x04\x1c\x040\x04\
-=\x04E\x042\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\
-\x06Manhwa\x07\x00\x00\x00\x06NlKi\
-nd\x01\x03\x00\x00\x00\x12\x00O\x00E\x00L\x00-\
-\x04<\x040\x04=\x043\x040\x08\x00\x00\x00\x00\x06\
-\x00\x00\x00\x09OEL-manga\x07\x00\x00\
-\x00\x06NlKind\x01\x03\x00\x00\x00\x0c\x04\x12\
-\x040\x04=\x04H\x04>\x04B\x08\x00\x00\x00\x00\x06\
-\x00\x00\x00\x07Oneshot\x07\x00\x00\x00\x06\
-NlKind\x01\x03\x00\x00\x00\x0c\x04\x14\x04@\
-\x04C\x043\x04>\x045\x08\x00\x00\x00\x00\x06\x00\x00\
-\x00\x05Other\x07\x00\x00\x00\x06NlKi\
-nd\x01\x03\x00\x00\x00\x0c\x04 \x040\x04=\x04>\
-\x041\x04M\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06Ra\
-nobe\x07\x00\x00\x00\x06NlKind\x01\
-\x03\x00\x00\x00\x10\x04 \x04C\x04:\x04>\x04<\x04\
-8\x04:\x04A\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07R\
-ucomic\x07\x00\x00\x00\x06NlKin\
-d\x01\x03\x00\x00\x00\x0e\x04 \x04C\x04<\x040\x04\
-=\x043\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07R\
-umanga\x07\x00\x00\x00\x06NlKin\
-d\x01\x03\x00\x00\x00\x1a\x04\x1d\x045\x00 \x04>\x04\
-?\x04@\x045\x044\x045\x04;\x045\x04=\x04\
->\x08\x00\x00\x00\x00\x06\x00\x00\x00\x09Undef\
-ined\x07\x00\x00\x00\x06NlKind\x01\
-\x03\x00\x00\x00\x1e\x04\x17\x040\x04?\x040\x044\x04\
-=\x04K\x049\x00 \x04:\x04>\x04<\x048\x04\
-:\x04A\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0dWes\
-tern comic\x07\x00\x00\x00\x06N\
-lKind\x01\x03\x00\x00\x00\x14\x04\x10\x04=\x04\
-3\x04;\x048\x049\x04A\x04:\x048\x049\x08\
-\x00\x00\x00\x00\x06\x00\x00\x00\x07English\
-\x07\x00\x00\x00\x0aNlLanguage\x01\
-\x03\x00\x00\x00\x10\x04/\x04?\x04>\x04=\x04A\x04\
-:\x048\x049\x08\x00\x00\x00\x00\x06\x00\x00\x00\x08J\
-apanese\x07\x00\x00\x00\x0aNlLa\
-nguage\x01\x03\x00\x00\x00\x0e\x04 \x04C\
-\x04A\x04A\x04:\x048\x049\x08\x00\x00\x00\x00\x06\
-\x00\x00\x00\x07Russian\x07\x00\x00\x00\x0a\
-NlLanguage\x01\x03\x00\x00\x00\x14\
-\x04#\x04:\x04@\x040\x048\x04=\x04A\x04:\
-\x048\x049\x08\x00\x00\x00\x00\x06\x00\x00\x00\x09Uk\
-rainian\x07\x00\x00\x00\x0aNlLa\
-nguage\x01\x03\x00\x00\x00\x1a\x04\x1d\x045\
-\x00 \x04>\x04?\x04@\x045\x044\x045\x04;\
-\x045\x04=\x04>\x08\x00\x00\x00\x00\x06\x00\x00\x00\x09\
-Undefined\x07\x00\x00\x00\x0aNl\
-Language\x01\x03\x00\x00\x00\x08\x04\x13\
-\x04;\x040\x042\x08\x00\x00\x00\x00\x06\x00\x00\x00\x08\
-Chapters\x07\x00\x00\x00\x05Oth\
-er\x01\x03\x00\x00\x00\x10\x04!\x04B\x04@\x040\
-\x04=\x048\x04F\x040\x08\x00\x00\x00\x00\x06\x00\x00\
-\x00\x04Page\x07\x00\x00\x00\x05Other\
-\x01\x03\x00\x00\x00(\x04!\x04B\x04@\x040\x04=\
-\x048\x04F\x040\x00 \x047\x040\x043\x04@\
-\x04C\x046\x040\x045\x04B\x04A\x04O\x08\x00\
-\x00\x00\x00\x06\x00\x00\x00\x0fPage is \
-loading\x07\x00\x00\x00\x05Othe\
-r\x01\x03\x00\x00\x00\x0e\x04 \x045\x049\x04B\x04\
-8\x04=\x043\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06R\
-ating\x07\x00\x00\x00\x05Other\x01\
-\x03\x00\x00\x00\x0a\x04\x12\x04>\x049\x04B\x048\x08\
-\x00\x00\x00\x00\x06\x00\x00\x00\x07Sign in\
-\x07\x00\x00\x00\x05Other\x01\x03\x00\x00\x00\x0c\
-\x04!\x04B\x040\x04B\x04C\x04A\x08\x00\x00\x00\
-\x00\x06\x00\x00\x00\x06Status\x07\x00\x00\x00\
-\x05Other\x01\x03\x00\x00\x00\x0a\x04\x22\x04>\
-\x04<\x04>\x042\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07\
-Volumes\x07\x00\x00\x00\x05Othe\
-r\x01\x03\x00\x00\x00\x0c\x04\x18\x047\x044\x040\x04\
-=\x04>\x08\x00\x00\x00\x00\x06\x00\x00\x00\x09Com\
-pleted\x07\x00\x00\x00\x06Statu\
-s\x01\x03\x00\x00\x00\x0e\x04\x12\x04K\x04E\x04>\x04\
-4\x048\x04B\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07O\
-ngoing\x07\x00\x00\x00\x06Statu\
-s\x01\x88\x00\x00\x00\x0d\x11\x01\xfd)\x0b\xff\x14\x02\x04\
-\xfd,\x0a\x13\
-\x00\x00\x15\x0d\
-<\
-\xb8d\x18\xca\xef\x9c\x95\xcd!\x1c\xbf`\xa1\xbd\xdd\xa7\
-\x00\x00\x00\x05uk_UAB\x00\x00\x02h\x00\x05\
- D\x00\x00\x03\xfa\x00\x050\xa4\x00\x00\x018\x00\x05\
-2\xde\x00\x00\x0b\x0e\x00\x057\xfe\x00\x00\x06G\x00\x05\
-g\xd5\x00\x00\x10\xd6\x00 \x7fy\x00\x00\x06\xad\x00 \
-\xfdw\x00\x00\x11\x04\x00Hw9\x00\x00\x02\x98\x00J\
-c\xf3\x00\x00\x0c\xb2\x00S]\xfe\x00\x00\x01e\x00S\
-\x84\xd1\x00\x00\x0dd\x00V\x8a\xc2\x00\x00\x04\x80\x00V\
-\xae\xc2\x00\x00\x0eN\x00X\xc9\xc4\x00\x00\x05y\x00\xaa\
-\xb4\x93\x00\x00\x01\xed\x00\xbf\xf1\xf4\x00\x00\x00\x80\x01'\
-\xda.\x00\x00\x0b\xbc\x01\xa5\xcc{\x00\x00\x09\x89\x02\xf9\
-\x88\xc9\x00\x00\x06\x0d\x03{\x0a~\x00\x00\x0aF\x03\xdb\
-sg\x00\x00\x04\x1d\x03\xdbsg\x00\x00\x06x\x04r\
-\x80\xd4\x00\x00\x03.\x04r\x80\xd4\x00\x00\x12\x0d\x04\x98\
-I\xbc\x00\x00\x00K\x04\xac,\xa5\x00\x00\x00\xcc\x04\xb6\
-\xc0\xfe\x00\x00\x0c\xde\x04\xc0RN\x00\x00\x09\xf2\x058\
-O\xb1\x00\x00\x0d\x8e\x058O\xd1\x00\x00\x0d\xbd\x05\x88\
-U\x85\x00\x00\x0ev\x05\x88\xb0G\x00\x00\x01\xbe\x05\x88\
-\xb0G\x00\x00\x11[\x05\x90_\xc2\x00\x00\x08S\x05\xaa\
-\x8b\xc3\x00\x00\x11\xb6\x05\xb5\x1b\xbe\x00\x00\x0bb\x05\xc6\
-\xa8\xa5\x00\x00\x02i\x06\x06\xf6t\x00\x00\x04L\x06!\
-\xd8#\x00\x00\x0d\x0f\x06L\x9f4\x00\x00\x0e \x06N\
-`\x17\x00\x00\x12=\x06\xc2\xecl\x00\x00\x08\xf8\x07(\
-T\xe4\x00\x00\x04\xb2\x08^\x80\x03\x00\x00\x07\xdb\x08h\
-I\x95\x00\x00\x0f\xc1\x08~2\x13\x00\x00\x02\xf9\x08\x80\
-\x13^\x00\x00\x100\x08\x9e=\xd9\x00\x00\x06\xe5\x08\xa8\
-1s\x00\x00\x02\xc8\x08\xaa\xe3\xe4\x00\x00\x01\x8f\x08\xb1\
-D~\x00\x00\x0a\xb3\x08\xb2(\x07\x00\x00\x04\xe8\x08\xb7\
-\xb0\x17\x00\x00\x05\x1d\x08\xc2\x8a\xe4\x00\x00\x05G\x09\x9a\
-\xc0\x05\x00\x00\x00\x00\x09\xbac\xa3\x00\x00\x0e\xa3\x09\xc3\
-\x84\x81\x00\x00\x0e\xd5\x09\xca\x9f.\x00\x00\x0f\xf8\x09\xfe\
-\x06\xae\x00\x00\x02;\x09\xfe\x06\xae\x00\x00\x05\xad\x09\xfe\
-\x06\xae\x00\x00\x11\x89\x0aU\x903\x00\x00\x074\x0a\xbc\
-\xb8\xf4\x00\x00\x0f\x05\x0a\xbc\xb8\xf4\x00\x00\x10l\x0b\x96\
-v\xf4\x00\x00\x03b\x0c\x1c\xf5$\x00\x00\x03\xbc\x0cN\
-0\xd8\x00\x00\x0f\x87\x0cf\xa1u\x00\x00\x00\xfd\x0c\xe4\
-\xbb\xc9\x00\x00\x093\x0d\x03\xac\xd3\x00\x00\x03\x8e\x0dc\
-\xc3\x93\x00\x00\x11\xe2\x0do\x06\x14\x00\x00\x07\x8c\x0d\xa5\
-\x0f\x13\x00\x00\x0fA\x0e\x14\xdds\x00\x00\x08\xa1\x0e\x87\
-\xa8\x03\x00\x00\x10\xac\x0f\x0a\xb6\xd9\x00\x00\x05\xd9\x0fC\
-7A\x00\x00\x0d\xeai\x00\x00\x12o\x03\x00\x00\x00\x1e\
-\x04\x1a\x04>\x044\x00 \x040\x042\x04B\x04>\
-\x04@\x048\x047\x040\x04F\x04V\x04W\x08\x00\
-\x00\x00\x00\x06\x00\x00\x00\x12Authoriz\
-ation code\x07\x00\x00\x00\x06D\
-ialog\x01\x03\x00\x00\x00\x14\x04!\x04:\x04\
-0\x04A\x04C\x042\x040\x04=\x04=\x04O\x08\
-\x00\x00\x00\x00\x06\x00\x00\x00\x06Cancel\x07\
-\x00\x00\x00\x06Dialog\x01\x03\x00\x00\x00$\
-\x04\x1f\x04@\x04>\x04G\x048\x04B\x040\x04=\
-\x04>\x00 \x04@\x04>\x047\x044\x04V\x04;\
-\x04V\x042\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0dCh\
-apters read\x07\x00\x00\x00\x06\
-Dialog\x01\x03\x00\x00\x00\x10\x04\x12\x048\
-\x04;\x04C\x04G\x048\x04B\x048\x08\x00\x00\x00\
-\x00\x06\x00\x00\x00\x06Delete\x07\x00\x00\x00\
-\x06Dialog\x01\x03\x00\x00\x00\x18\x04\x1e\x04\
-B\x04@\x048\x04<\x040\x04B\x048\x00 \x04\
-:\x04>\x044\x08\x00\x00\x00\x00\x06\x00\x00\x00\x08G\
-et code\x07\x00\x00\x00\x06Dial\
-og\x01\x03\x00\x00\x00\x0e\x04\x1f\x045\x04@\x045\
-\x04;\x04V\x04:\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04\
-List\x07\x00\x00\x00\x06Dialog\x01\
-\x03\x00\x00\x00\x0a\x04\x1b\x04>\x043\x04V\x04=\x08\
-\x00\x00\x00\x00\x06\x00\x00\x00\x05Login\x07\x00\
-\x00\x00\x06Dialog\x01\x03\x00\x00\x00\x0c\x04\
-\x1f\x040\x04@\x04>\x04;\x04L\x08\x00\x00\x00\x00\
-\x06\x00\x00\x00\x08Password\x07\x00\x00\
-\x00\x06Dialog\x01\x03\x00\x00\x00\x0e\x04 \
-\x045\x049\x04B\x048\x04=\x043\x08\x00\x00\x00\
-\x00\x06\x00\x00\x00\x06Rating\x07\x00\x00\x00\
-\x06Dialog\x01\x03\x00\x00\x00&\x04\x1f\x04\
->\x04:\x040\x047\x04C\x042\x040\x04B\x04\
-8\x00 \x04A\x04?\x04>\x049\x04;\x045\x04\
-@\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0dSho\
-w spoilers\x07\x00\x00\x00\x06D\
-ialog\x01\x03\x00\x00\x00\x0c\x04#\x042\x04\
-V\x049\x04B\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\
-\x07Sign in\x07\x00\x00\x00\x06Dia\
-log\x01\x03\x00\x00\x00\x0e\x04\x1e\x04=\x04>\x04\
-2\x048\x04B\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\
-\x06Update\x07\x00\x00\x00\x06Dial\
-og\x01\x03\x00\x00\x00\x12\x04\x1f\x04@\x048\x04<\
-\x045\x04=\x048\x04B\x04L\x08\x00\x00\x00\x00\x06\
-\x00\x00\x00\x05Apply\x07\x00\x00\x00\x04Fo\
-rm\x01\x03\x00\x00\x00\x10\x04\x1a\x040\x04B\x040\
-\x04;\x04>\x043\x048\x08\x00\x00\x00\x00\x06\x00\x00\
-\x00\x08Catalogs\x07\x00\x00\x00\x04F\
-orm\x01\x03\x00\x00\x00\x12\x04\x1f\x045\x04@\x04\
-A\x04>\x04=\x040\x046\x04V\x08\x00\x00\x00\x00\
-\x06\x00\x00\x00\x0aCharacters\x07\
-\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\x12\x04\x1f\
-\x04@\x04>\x04G\x048\x04B\x040\x04=\x04>\
-\x08\x00\x00\x00\x00\x06\x00\x00\x00\x09Comple\
-ted\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\
-\x00\x0c\x04\x1a\x048\x04=\x04C\x04B\x04>\x08\x00\
-\x00\x00\x00\x06\x00\x00\x00\x07Dropped\x07\
-\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\x0e\x04$\
-\x04V\x04;\x04L\x04B\x04@\x048\x08\x00\x00\x00\
-\x00\x06\x00\x00\x00\x07Filters\x07\x00\x00\
-\x00\x04Form\x01\x03\x00\x00\x00\x1a\x04!\x04?\
-\x048\x04A\x04>\x04:\x00 \x046\x040\x04=\
-\x04@\x04V\x042\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0b\
-Genres list\x07\x00\x00\x00\x04\
-Form\x01\x03\x00\x00\x00\x06\x04\x22\x048\x04?\
-\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04Kind\x07\x00\
-\x00\x00\x04Form\x01\x03\x00\x00\x00\x0c\x00N\x00\
-l\x00i\x00g\x00h\x00t\x08\x00\x00\x00\x00\x06\x00\
-\x00\x00\x0aMainWindow\x07\x00\x00\
-\x00\x04Form\x01\x03\x00\x00\x00\x14\x04\x12\x04V\
-\x044\x04:\x04;\x040\x044\x045\x04=\x04>\
-\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07On hol\
-d\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\x00\x14\
-\x04!\x04>\x04@\x04B\x04C\x042\x040\x04=\
-\x04=\x04O\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05Or\
-der\x07\x00\x00\x00\x04Form\x01\x03\x00\x00\
-\x00\x16\x04\x17\x040\x04?\x04;\x040\x04=\x04>\
-\x042\x040\x04=\x04>\x08\x00\x00\x00\x00\x06\x00\x00\
-\x00\x07Planned\x07\x00\x00\x00\x04Fo\
-rm\x01\x03\x00\x00\x00\x12\x04\x1f\x045\x04@\x045\
-\x04G\x048\x04B\x04C\x04N\x08\x00\x00\x00\x00\x06\
-\x00\x00\x00\x0aRe-reading\x07\x00\
-\x00\x00\x04Form\x01\x03\x00\x00\x00\x0a\x04'\x04\
-8\x04B\x040\x04N\x08\x00\x00\x00\x00\x06\x00\x00\x00\
-\x07Reading\x07\x00\x00\x00\x04For\
-m\x01\x03\x00\x00\x00\x12\x04\x1f\x04>\x042\x00'\x04\
-O\x047\x040\x04=\x045\x08\x00\x00\x00\x00\x06\x00\
-\x00\x00\x07Related\x07\x00\x00\x00\x04F\
-orm\x01\x03\x00\x00\x00\x16\x04\x17\x040\x04A\x04\
-B\x04>\x04A\x04C\x042\x040\x04B\x048\x08\
-\x00\x00\x00\x00\x06\x00\x00\x00\x05Reset\x07\x00\
-\x00\x00\x04Form\x01\x03\x00\x00\x00\x0c\x04#\x04\
-2\x04V\x049\x04B\x048\x08\x00\x00\x00\x00\x06\x00\
-\x00\x00\x07Sign in\x07\x00\x00\x00\x04F\
-orm\x01\x03\x00\x00\x00\x0e\x04\x06\x04A\x04B\x04\
->\x04@\x04V\x04O\x08\x00\x00\x00\x00\x06\x00\x00\x00\
-\x07History\x07\x00\x00\x00\x0aMai\
-nWindow\x01\x03\x00\x00\x00\x14\x04\x11\x04\
-V\x041\x04;\x04V\x04>\x04B\x045\x04:\x04\
-0\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Libra\
-ry\x07\x00\x00\x00\x0aMainWindo\
-w\x01\x03\x00\x00\x00\x0e\x04\x13\x04>\x04;\x04>\x04\
-2\x04=\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04M\
-ain\x07\x00\x00\x00\x0aMainWind\
-ow\x01\x03\x00\x00\x00\x0c\x00N\x00l\x00i\x00g\
-\x00h\x00t\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0aMa\
-inWindow\x07\x00\x00\x00\x0aMai\
-nWindow\x01\x03\x00\x00\x00\x10\x04(\x04\
-8\x04:\x048\x04<\x04>\x04@\x048\x08\x00\x00\
-\x00\x00\x06\x00\x00\x00\x09Shikimori\
-\x07\x00\x00\x00\x0aMainWindow\x01\
-\x03\x00\x00\x00(\x04\x14\x04>\x044\x040\x04B\x04\
-8\x00 \x044\x04>\x00 \x041\x04V\x041\x04\
-;\x04V\x04>\x04B\x045\x04:\x048\x08\x00\x00\
-\x00\x00\x06\x00\x00\x00\x0eAdd to Li\
-brary\x07\x00\x00\x00\x04Menu\x01\x03\
-\x00\x00\x00.\x04\x1e\x04G\x048\x04A\x04B\x048\
-\x04B\x048\x00 \x04;\x04>\x04:\x040\x04;\
-\x04L\x04=\x04V\x00 \x04D\x040\x049\x04;\
-\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\x11Clea\
-r local files\x07\x00\x00\
-\x00\x04Menu\x01\x03\x00\x00\x00*\x04\x12\x04V\
-\x044\x047\x04=\x040\x04G\x048\x04B\x048\
-\x00 \x04?\x04@\x04>\x04G\x048\x04B\x040\
-\x04=\x048\x04<\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0c\
-Mark as read\x07\x00\x00\x00\
-\x04Menu\x01\x03\x00\x00\x00F\x04\x12\x04V\x04\
-4\x047\x04=\x040\x04G\x048\x04B\x048\x00\
- \x04?\x04@\x04>\x04G\x048\x04B\x040\x04\
-=\x048\x04<\x00 \x04C\x04A\x04V\x00 \x04\
-?\x04>\x04?\x045\x04@\x045\x044\x04=\x04\
-V\x08\x00\x00\x00\x00\x06\x00\x00\x00\x19Mark \
-as read all prev\
-ious\x07\x00\x00\x00\x04Menu\x01\x03\x00\
-\x00\x00&\x04\x12\x04V\x044\x04:\x04@\x048\x04\
-B\x048\x00 \x042\x00 \x041\x04@\x040\x04\
-C\x047\x045\x04@\x04V\x08\x00\x00\x00\x00\x06\x00\
-\x00\x00\x0fOpen in brows\
-er\x07\x00\x00\x00\x04Menu\x01\x03\x00\x00\x00\
-.\x04\x12\x04V\x044\x04:\x04@\x048\x04B\x04\
-8\x00 \x04;\x04>\x04:\x040\x04;\x04L\x04\
-=\x04V\x00 \x04D\x040\x049\x04;\x048\x08\
-\x00\x00\x00\x00\x06\x00\x00\x00\x10Open lo\
-cal files\x07\x00\x00\x00\x04Me\
-nu\x01\x03\x00\x00\x00\x18\x04\x12\x048\x044\x040\
-\x04;\x048\x04B\x048\x00 \x042\x04A\x045\
-\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0aRemove\
- all\x07\x00\x00\x00\x04Menu\x01\x03\x00\
-\x00\x00*\x04\x12\x048\x044\x040\x04;\x048\x04\
-B\x048\x00 \x047\x00 \x041\x04V\x041\x04\
-;\x04V\x04>\x04B\x045\x04:\x048\x08\x00\x00\
-\x00\x00\x06\x00\x00\x00\x13Remove fr\
-om library\x07\x00\x00\x00\x04M\
-enu\x01\x03\x00\x00\x00@\x04\x12\x048\x044\x04\
-0\x04;\x048\x04B\x048\x00 \x042\x04V\x04\
-4\x04<\x04V\x04B\x04:\x04C\x00 \x04?\x04\
-@\x04>\x00 \x04?\x04@\x04>\x04G\x048\x04\
-B\x040\x04=\x04=\x04O\x08\x00\x00\x00\x00\x06\x00\
-\x00\x00\x10Remove read m\
-ark\x07\x00\x00\x00\x04Menu\x01\x03\x00\x00\
-\x00&\x04\x1f\x045\x04@\x045\x042\x04V\x04@\
-\x04:\x040\x00 \x04>\x04=\x04>\x042\x04;\
-\x045\x04=\x04L\x00.\x08\x00\x00\x00\x00\x06\x00\x00\
-\x00\x12Check for upda\
-tes.\x07\x00\x00\x00\x07Message\
-\x01\x03\x00\x00\x006\x04\x1f\x04>\x04<\x048\x04;\
-\x04:\x040\x00 \x04?\x045\x04@\x045\x042\
-\x04V\x04@\x04:\x048\x00 \x04>\x04=\x04>\
-\x042\x04;\x045\x04=\x04L\x00.\x08\x00\x00\x00\
-\x00\x06\x00\x00\x00\x1bError chec\
-king for updates\
-.\x07\x00\x00\x00\x07Message\x01\x03\x00\
-\x00\x00$\x04$\x040\x049\x04;\x048\x00 \x00\
-{\x00}\x00 \x042\x048\x044\x040\x04;\x04\
-5\x04=\x04V\x00.\x08\x00\x00\x00\x00\x06\x00\x00\x00\
-\x1bFiles {} have b\
-een removed.\x07\x00\x00\x00\
-\x07Message\x01\x03\x00\x00\x00 \x04\x1c\
-\x040\x04=\x043\x040\x00 \x00{\x00}\x00 \
-\x044\x04>\x044\x040\x04=\x04>\x00.\x08\x00\
-\x00\x00\x00\x06\x00\x00\x00\x18Manga {}\
- has been added.\
-\x07\x00\x00\x00\x07Message\x01\x03\x00\x00\
-\x00$\x04\x1c\x040\x04=\x043\x040\x00 \x00{\
-\x00}\x00 \x042\x048\x044\x040\x04;\x045\
-\x04=\x04>\x00.\x08\x00\x00\x00\x00\x06\x00\x00\x00\x1a\
-Manga {} has bee\
-n deleted.\x07\x00\x00\x00\x07M\
-essage\x01\x03\x00\x00\x00\x8c\x04\x1d\x04>\
-\x042\x040\x00 \x042\x045\x04@\x04A\x04V\
-\x04O\x00 \x00{\x00r\x00e\x00s\x00u\x00l\
-\x00t\x00}\x00 \x044\x04>\x04A\x04B\x04C\
-\x04?\x04=\x040\x00!\x00 \x04\x12\x048\x00 \
-\x042\x048\x04:\x04>\x04@\x048\x04A\x04B\
-\x04>\x042\x04C\x04T\x04B\x045\x00 \x042\
-\x045\x04@\x04A\x04V\x04N\x00 \x00{\x00A\
-\x00P\x00P\x00_\x00V\x00E\x00R\x00S\x00I\
-\x00O\x00N\x00}\x00.\x08\x00\x00\x00\x00\x06\x00\x00\
-\x00NNew version {r\
-esult} is availa\
-ble! You are cur\
-rently on versio\
-n {APP_VERSION}.\
-\x07\x00\x00\x00\x07Message\x01\x03\x00\x00\
-\x00\x0c\x04\x1a\x04>\x04<\x04V\x04:\x04A\x08\x00\
-\x00\x00\x00\x06\x00\x00\x00\x05Comic\x07\x00\x00\
-\x00\x06NlKind\x01\x03\x00\x00\x00\x10\x04\x14\
-\x04>\x044\x047\x048\x04=\x04A\x048\x08\x00\
-\x00\x00\x00\x06\x00\x00\x00\x06Doujin\x07\x00\
-\x00\x00\x06NlKind\x01\x03\x00\x00\x00*\x04\
-\x06\x04=\x044\x04>\x04=\x045\x047\x04V\x04\
-9\x04A\x04L\x04:\x048\x049\x00 \x04:\x04\
->\x04<\x04V\x04:\x04A\x08\x00\x00\x00\x00\x06\x00\
-\x00\x00\x10Indonesian co\
-mic\x07\x00\x00\x00\x06NlKind\x01\x03\
-\x00\x00\x00\x0a\x04\x1c\x040\x04=\x043\x040\x08\x00\
-\x00\x00\x00\x06\x00\x00\x00\x05Manga\x07\x00\x00\
-\x00\x06NlKind\x01\x03\x00\x00\x00\x0e\x04\x1c\
-\x040\x04=\x04L\x04E\x04C\x040\x08\x00\x00\x00\
-\x00\x06\x00\x00\x00\x06Manhua\x07\x00\x00\x00\
-\x06NlKind\x01\x03\x00\x00\x00\x0c\x04\x1c\x04\
-0\x04=\x04E\x042\x040\x08\x00\x00\x00\x00\x06\x00\
-\x00\x00\x06Manhwa\x07\x00\x00\x00\x06Nl\
-Kind\x01\x03\x00\x00\x00\x12\x00O\x00E\x00L\
-\x00-\x04<\x040\x04=\x043\x040\x08\x00\x00\x00\
-\x00\x06\x00\x00\x00\x09OEL-manga\x07\
-\x00\x00\x00\x06NlKind\x01\x03\x00\x00\x00\x0c\
-\x04\x12\x040\x04=\x04H\x04>\x04B\x08\x00\x00\x00\
-\x00\x06\x00\x00\x00\x07Oneshot\x07\x00\x00\
-\x00\x06NlKind\x01\x03\x00\x00\x00\x08\x04\x06\
-\x04=\x04H\x045\x08\x00\x00\x00\x00\x06\x00\x00\x00\x05\
-Other\x07\x00\x00\x00\x06NlKind\
-\x01\x03\x00\x00\x00\x0c\x04 \x040\x04=\x04>\x041\
-\x04M\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06Rano\
-be\x07\x00\x00\x00\x06NlKind\x01\x03\x00\
-\x00\x00\x10\x04 \x04C\x04:\x04>\x04<\x04V\x04\
-:\x04A\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Ruc\
-omic\x07\x00\x00\x00\x06NlKind\x01\
-\x03\x00\x00\x00\x0e\x04 \x04C\x04<\x040\x04=\x04\
-3\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07Rum\
-anga\x07\x00\x00\x00\x06NlKind\x01\
-\x03\x00\x00\x00\x18\x04\x1d\x045\x00 \x042\x048\x04\
-7\x04=\x040\x04G\x045\x04=\x04>\x08\x00\x00\
-\x00\x00\x06\x00\x00\x00\x09Undefined\
-\x07\x00\x00\x00\x06NlKind\x01\x03\x00\x00\x00\
-\x1e\x04\x17\x040\x04E\x04V\x044\x04=\x048\x04\
-9\x00 \x04:\x04>\x04<\x04V\x04:\x04A\x08\
-\x00\x00\x00\x00\x06\x00\x00\x00\x0dWestern\
- comic\x07\x00\x00\x00\x06NlKin\
-d\x01\x03\x00\x00\x00\x14\x04\x10\x04=\x043\x04;\x04\
-V\x049\x04A\x04L\x04:\x040\x08\x00\x00\x00\x00\
-\x06\x00\x00\x00\x07English\x07\x00\x00\x00\
-\x0aNlLanguage\x01\x03\x00\x00\x00\
-\x10\x04/\x04?\x04>\x04=\x04A\x04L\x04:\x04\
-0\x08\x00\x00\x00\x00\x06\x00\x00\x00\x08Japan\
-ese\x07\x00\x00\x00\x0aNlLangua\
-ge\x01\x03\x00\x00\x00\x12\x04 \x04>\x04A\x04V\
-\x049\x04A\x04L\x04:\x040\x08\x00\x00\x00\x00\x06\
-\x00\x00\x00\x07Russian\x07\x00\x00\x00\x0a\
-NlLanguage\x01\x03\x00\x00\x00\x14\
-\x04#\x04:\x04@\x040\x04W\x04=\x04A\x04L\
-\x04:\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\x09Uk\
-rainian\x07\x00\x00\x00\x0aNlLa\
-nguage\x01\x03\x00\x00\x00\x18\x04\x1d\x045\
-\x00 \x042\x048\x047\x04=\x040\x04G\x045\
-\x04=\x04>\x08\x00\x00\x00\x00\x06\x00\x00\x00\x09Un\
-defined\x07\x00\x00\x00\x0aNlLa\
-nguage\x01\x03\x00\x00\x00\x08\x04\x13\x04;\
-\x040\x042\x08\x00\x00\x00\x00\x06\x00\x00\x00\x08Ch\
-apters\x07\x00\x00\x00\x05Other\
-\x01\x03\x00\x00\x00\x10\x04!\x04B\x04>\x04@\x04V\
-\x04=\x04:\x040\x08\x00\x00\x00\x00\x06\x00\x00\x00\x04\
-Page\x07\x00\x00\x00\x05Other\x01\x03\
-\x00\x00\x00.\x04!\x04B\x04>\x04@\x04V\x04=\
-\x04:\x040\x00 \x047\x040\x042\x040\x04=\
-\x04B\x040\x046\x04C\x04T\x04B\x04L\x04A\
-\x04O\x08\x00\x00\x00\x00\x06\x00\x00\x00\x0fPage\
- is loading\x07\x00\x00\x00\x05\
-Other\x01\x03\x00\x00\x00\x0e\x04 \x045\x04\
-9\x04B\x048\x04=\x043\x08\x00\x00\x00\x00\x06\x00\
-\x00\x00\x06Rating\x07\x00\x00\x00\x05Ot\
-her\x01\x03\x00\x00\x00\x0c\x04#\x042\x04V\x04\
-9\x04B\x048\x08\x00\x00\x00\x00\x06\x00\x00\x00\x07S\
-ign in\x07\x00\x00\x00\x05Other\
-\x01\x03\x00\x00\x00\x0c\x04!\x04B\x040\x04B\x04C\
-\x04A\x08\x00\x00\x00\x00\x06\x00\x00\x00\x06Stat\
-us\x07\x00\x00\x00\x05Other\x01\x03\x00\x00\
-\x00\x0a\x04\x22\x04>\x04<\x04V\x042\x08\x00\x00\x00\
-\x00\x06\x00\x00\x00\x07Volumes\x07\x00\x00\
-\x00\x05Other\x01\x03\x00\x00\x00\x0c\x04\x12\x04\
-8\x044\x040\x04=\x04>\x08\x00\x00\x00\x00\x06\x00\
-\x00\x00\x09Completed\x07\x00\x00\x00\
-\x06Status\x01\x03\x00\x00\x00\x10\x04\x12\x04\
-8\x04E\x04>\x044\x048\x04B\x04L\x08\x00\x00\
-\x00\x00\x06\x00\x00\x00\x07Ongoing\x07\x00\
-\x00\x00\x06Status\x01\x88\x00\x00\x00\x0d\x11\
-\x01\xfd)\x0b\xff\x14\x02\x04\xfd,\x0a\x13\
-"
-
-qt_resource_name = b"\
-\x00\x0c\
-\x0d\xfc\x11\x13\
-\x00t\
-\x00r\x00a\x00n\x00s\x00l\x00a\x00t\x00i\x00o\x00n\x00s\
-\x00\x0d\
-\x06\x0f*\xbb\
-\x00a\
-\x00c\x00t\x00i\x00o\x00n\x00s\x00_\x00b\x00l\x00a\x00c\x00k\
-\x00\x0a\
-\x0dQ\xf6\xd3\
-\x00l\
-\x00a\x00n\x00g\x00_\x00i\x00c\x00o\x00n\x00s\
-\x00\x09\
-\x0dm\x1e%\
-\x00p\
-\x00n\x00g\x00_\x00w\x00h\x00i\x00t\x00e\
-\x00\x0d\
-\x06;\xfd\x85\
-\x00a\
-\x00c\x00t\x00i\x00o\x00n\x00s\x00_\x00w\x00h\x00i\x00t\x00e\
-\x00\x04\
-\x00\x06\xa8\xa1\
-\x00d\
-\x00a\x00t\x00a\
-\x00\x05\
-\x00o\xa6S\
-\x00i\
-\x00c\x00o\x00n\x00s\
-\x00\x07\
-\x09\xcb\xb6\x93\
-\x00b\
-\x00u\x00t\x00t\x00o\x00n\x00s\
-\x00\x0e\
-\x0f\xc9[e\
-\x00s\
-\x00v\x00g\x00_\x002\x004\x00d\x00p\x00_\x00w\x00h\x00i\x00t\x00e\
-\x00\x07\
-\x07\xab\x06\x93\
-\x00a\
-\x00c\x00t\x00i\x00o\x00n\x00s\
-\x00\x0d\
-\x05l\x1b'\
-\x00s\
-\x00h\x00i\x00k\x00i\x00m\x00o\x00r\x00i\x00.\x00s\x00v\x00g\
-\x00\x08\
-\x0aaZ\xa7\
-\x00i\
-\x00c\x00o\x00n\x00.\x00p\x00n\x00g\
-\x00\x04\
-\x00\x07(G\
-\x00l\
-\x00a\x00n\x00g\
-\x00\x06\
-\x07\x98Z\xc7\
-\x00r\
-\x00u\x00.\x00s\x00v\x00g\
-\x00\x06\
-\x06\xd5Z\xc7\
-\x00g\
-\x00b\x00.\x00s\x00v\x00g\
-\x00\x06\
-\x07\x13Z\xc7\
-\x00j\
-\x00p\x00.\x00s\x00v\x00g\
-\x00\x06\
-\x07\xb4Z\xc7\
-\x00u\
-\x00a\x00.\x00s\x00v\x00g\
-\x00\x0e\
-\x0f\xfd\x8c[\
-\x00s\
-\x00v\x00g\x00_\x002\x004\x00d\x00p\x00_\x00b\x00l\x00a\x00c\x00k\
-\x00\x02\
-\x00\x00\x07\xbb\
-\x00u\
-\x00k\
-\x00\x02\
-\x00\x00\x07\x95\
-\x00r\
-\x00u\
-\x00\x05\
-\x00y\x85}\
-\x00r\
-\x00u\x00.\x00q\x00m\
-\x00\x05\
-\x00{\xe5}\
-\x00u\
-\x00k\x00.\x00q\x00m\
-"
-
-qt_resource_struct = b"\
-\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x01\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\x1e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1c\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00p\x00\x02\x00\x00\x00\x01\x00\x00\x00\x16\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00>\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0f\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00X\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0c\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x06\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\x90\x00\x02\x00\x00\x00\x01\x00\x00\x00\x07\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x08\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x01\xb0\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0b\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x01\xa6\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0a\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x01\xca\x00\x00\x00\x00\x00\x01\x00\x007?\
-\x00\x00\x01\x8e\xe8m\x15e\
-\x00\x00\x01\xba\x00\x00\x00\x00\x00\x01\x00\x00\x22V\
-\x00\x00\x01\x8e\xe8m\x15c\
-\x00\x00\x00\x90\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0d\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\x9e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x0e\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x01\x18\x00\x00\x00\x00\x00\x01\x00\x00\x03\x8e\
-\x00\x00\x01\x89\x1a\xbc\xed\xd5\
-\x00\x00\x00\x90\x00\x02\x00\x00\x00\x01\x00\x00\x00\x10\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\x9e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x11\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x01.\x00\x02\x00\x00\x00\x04\x00\x00\x00\x12\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x01N\x00\x00\x00\x00\x00\x01\x00\x00\x17\xea\
-\x00\x00\x01\x89\x1a\xbc\xed\xd5\
-\x00\x00\x01`\x00\x00\x00\x00\x00\x01\x00\x00\x1c\xdb\
-\x00\x00\x01\x89\x1a\xbc\xed\xd5\
-\x00\x00\x01<\x00\x00\x00\x00\x00\x01\x00\x00\x16\xb5\
-\x00\x00\x01\x8dJ0\xc3\x91\
-\x00\x00\x01r\x00\x00\x00\x00\x00\x01\x00\x00\x1d\xd3\
-\x00\x00\x01\x8dJ0\xcb\xbc\
-\x00\x00\x00\x90\x00\x02\x00\x00\x00\x01\x00\x00\x00\x17\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\x9e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x18\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\xae\x00\x02\x00\x00\x00\x01\x00\x00\x00\x19\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\xc2\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1a\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\xe4\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1b\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\xf8\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
-\x00\x00\x01\x89\x1a\xbc\xed\xd1\
-\x00\x00\x00\x90\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1d\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\x9e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1e\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\xae\x00\x02\x00\x00\x00\x01\x00\x00\x00\x1f\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x01\x84\x00\x02\x00\x00\x00\x01\x00\x00\x00 \
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\xe4\x00\x02\x00\x00\x00\x01\x00\x00\x00!\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\xf8\x00\x00\x00\x00\x00\x01\x00\x00\x1e\xc8\
-\x00\x00\x01\x89\x1a\xbc\xed\xcd\
-"
-
-def qInitResources():
- QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
-
-def qCleanupResources():
- QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
-
-qInitResources()
diff --git a/nlightreader/consts/app.py b/nlightreader/consts/app.py
index 7a1ef3d0..9e5ff559 100644
--- a/nlightreader/consts/app.py
+++ b/nlightreader/consts/app.py
@@ -1,3 +1,3 @@
APP_NAME = "Nlight"
-APP_VERSION = "1.10.2"
+APP_VERSION = "1.10.4"
APP_BRANCH = "1.10"
diff --git a/nlightreader/consts/colors.py b/nlightreader/consts/colors.py
index 58781113..9934fdc6 100644
--- a/nlightreader/consts/colors.py
+++ b/nlightreader/consts/colors.py
@@ -1,8 +1,7 @@
from PySide6.QtGui import QColor
+from qfluentwidgets import FluentIcon
-class ItemsColors:
- READ = QColor("GREEN")
- UNREAD = QColor("RED")
- IN_LIBRARY = QColor("ORANGE")
- EMPTY = QColor(255, 255, 255, 0)
+class ItemsIcons:
+ READ = FluentIcon.ACCEPT_MEDIUM
+ UNREAD = FluentIcon.ACCEPT_MEDIUM.icon(color=QColor("RED"))
diff --git a/nlightreader/consts/enums.py b/nlightreader/consts/enums.py
index 674db3ef..f2333334 100644
--- a/nlightreader/consts/enums.py
+++ b/nlightreader/consts/enums.py
@@ -1,5 +1,5 @@
import logging
-from enum import Enum, unique
+from enum import IntEnum, unique
LIB_LISTS = (
"planned",
@@ -13,7 +13,7 @@
class Nl:
@unique
- class Language(Enum):
+ class Language(IntEnum):
undefined = 0
en = 1
ru = 2
@@ -46,14 +46,14 @@ def to_str(self) -> str:
return names[self.value]
@unique
- class CatalogType(Enum):
+ class CatalogType(IntEnum):
manga = 0
hentai_manga = 1
ranobe = 2
anime = 3
@unique
- class MangaKind(Enum):
+ class MangaKind(IntEnum):
undefined = 0
manga = 1
manhwa = 2
@@ -102,7 +102,7 @@ def to_str(self) -> str:
return names[self.value]
@unique
- class LibList(Enum):
+ class LibList(IntEnum):
planned = 0
completed = 1
reading = 2
@@ -111,7 +111,7 @@ class LibList(Enum):
dropped = 5
@classmethod
- def from_str(cls, string: str | None):
+ def from_str(cls, string: str):
string = string.lower()
if string in ("planned",):
return cls.planned
@@ -129,3 +129,34 @@ def from_str(cls, string: str | None):
def to_str(self) -> str:
return LIB_LISTS[self.value]
+
+ @unique
+ class MangaStatus(IntEnum):
+ undefined = 0
+ ongoing = 1
+ released = 2
+ frozen = 3
+
+ @classmethod
+ def from_str(cls, string: str | None):
+ if string is None or string.lower() in ("undefined", "неизвестно"):
+ return cls.undefined
+
+ string = string.lower()
+ if string in ("ongoing", "в процессе"):
+ return cls.ongoing
+ if string in ("released", "completed", "завершено"):
+ return cls.released
+ if string in ("frozen", "заморожено"):
+ return cls.frozen
+ logging.warning(f"Unknown manga status: {string}")
+ return cls.undefined
+
+ def to_str(self) -> str:
+ names = [
+ "Undefined",
+ "Ongoing",
+ "Released",
+ "Frozen",
+ ]
+ return names[self.value]
diff --git a/nlightreader/consts/files/__init__.py b/nlightreader/consts/files/__init__.py
index a01c09af..677cdaf8 100644
--- a/nlightreader/consts/files/__init__.py
+++ b/nlightreader/consts/files/__init__.py
@@ -1 +1 @@
-from .files import Icons, LangIcons, Translations, NlFluentIcons
+from .files import Icons, LangIcons, NlFluentIcons
diff --git a/nlightreader/consts/files/files.py b/nlightreader/consts/files/files.py
index c77105ae..cf7588cf 100644
--- a/nlightreader/consts/files/files.py
+++ b/nlightreader/consts/files/files.py
@@ -2,24 +2,16 @@
from qfluentwidgets import FluentIconBase, getIconColor, Theme
-import nlight_res_rc
-
class LangIcons:
- Gb = ":/lang_icons/data/icons/lang/gb.svg"
- Ru = ":/lang_icons/data/icons/lang/ru.svg"
- Jp = ":/lang_icons/data/icons/lang/jp.svg"
- Ua = ":/lang_icons/data/icons/lang/ua.svg"
-
-
-class Translations:
- En = ""
- Ru = ":/translations/data/translations/ru/ru.qm"
- Uk = ":/translations/data/translations/uk/uk.qm"
+ Gb = ":/lang_icons/icons/lang/gb.svg"
+ Ru = ":/lang_icons/icons/lang/ru.svg"
+ Jp = ":/lang_icons/icons/lang/jp.svg"
+ Ua = ":/lang_icons/icons/lang/ua.svg"
class Icons:
- App = ":/png_white/data/icons/icon.png"
+ App = ":/png_white/icons/icon.png"
class NlFluentIcons(FluentIconBase, Enum):
@@ -30,6 +22,6 @@ class NlFluentIcons(FluentIconBase, Enum):
def path(self, theme=Theme.AUTO):
return (
f":/actions_{getIconColor(theme)}"
- f"/data/icons/buttons/svg_24dp_{getIconColor(theme)}"
+ f"/icons/buttons/svg_24dp_{getIconColor(theme)}"
f"/actions/{self.value}.svg"
)
diff --git a/nlightreader/consts/items/remanga_items.py b/nlightreader/consts/items/remanga_items.py
index ace5bb83..1889d559 100644
--- a/nlightreader/consts/items/remanga_items.py
+++ b/nlightreader/consts/items/remanga_items.py
@@ -19,11 +19,11 @@ class RemangaItems(ParserItems):
]
KINDS = [
- {"value": 0} | Pk.MANGA,
- {"value": 1} | Pk.MANHWA,
- {"value": 2} | Pk.MANHUA,
- {"value": 3} | Pk.WESTERN_COMIC,
- {"value": 4} | Pk.RU_COMIC,
- {"value": 5} | Pk.INDONESIAN_COMIC,
- {"value": 6} | Pk.OTHER,
+ {"value": 1} | Pk.MANGA,
+ {"value": 2} | Pk.MANHWA,
+ {"value": 3} | Pk.MANHUA,
+ {"value": 4} | Pk.WESTERN_COMIC,
+ {"value": 5} | Pk.RU_COMIC,
+ {"value": 6} | Pk.INDONESIAN_COMIC,
+ {"value": 7} | Pk.OTHER,
]
diff --git a/nlightreader/consts/paths/__init__.py b/nlightreader/consts/paths/__init__.py
index f6bb41ad..ff05a2b8 100644
--- a/nlightreader/consts/paths/__init__.py
+++ b/nlightreader/consts/paths/__init__.py
@@ -1 +1 @@
-from .paths import TOKEN_PATH
+from .paths import TOKEN_PATH, APP_DATA_PATH
diff --git a/nlightreader/consts/paths/paths.py b/nlightreader/consts/paths/paths.py
index aa804d8a..a6e18bd1 100644
--- a/nlightreader/consts/paths/paths.py
+++ b/nlightreader/consts/paths/paths.py
@@ -2,4 +2,6 @@
from nlightreader.consts.app import APP_NAME
-TOKEN_PATH = platformdirs.user_data_path() / APP_NAME / "tokens"
+
+APP_DATA_PATH = platformdirs.user_data_path() / APP_NAME
+TOKEN_PATH = APP_DATA_PATH / "tokens"
diff --git a/nlightreader/consts/urls.py b/nlightreader/consts/urls.py
index 5d0434c0..08393fc1 100644
--- a/nlightreader/consts/urls.py
+++ b/nlightreader/consts/urls.py
@@ -21,8 +21,7 @@
URL_REMANGA = "https://remanga.org"
URL_REMANGA_API = "https://remanga.org/api"
-URL_NHENTAI = "https://nhentai.to"
-URL_NHENTAI_API = "https://nhentai.net/api"
+URL_NHENTAI = "https://nhentai.net"
URL_ALLHENTAI = "https://20.allhen.online"
URL_ALLHENTAI_API = ""
diff --git a/nlightreader/contexts/HistoryNote.py b/nlightreader/contexts/HistoryNote.py
index 045f4a7c..f0a42d87 100644
--- a/nlightreader/contexts/HistoryNote.py
+++ b/nlightreader/contexts/HistoryNote.py
@@ -1,6 +1,6 @@
from qfluentwidgets import Action, FluentIcon, RoundMenu
-from nlightreader.utils import translate
+from nlightreader.utils.translator import translate
class HistoryNoteMenu(RoundMenu):
diff --git a/nlightreader/contexts/LibraryManga.py b/nlightreader/contexts/LibraryManga.py
index 67d77a13..c5fbb35c 100644
--- a/nlightreader/contexts/LibraryManga.py
+++ b/nlightreader/contexts/LibraryManga.py
@@ -1,6 +1,6 @@
from qfluentwidgets import Action, FluentIcon, RoundMenu
-from nlightreader.utils import translate
+from nlightreader.utils.translator import translate
class LibraryMangaMenu(RoundMenu):
diff --git a/nlightreader/contexts/ReadMark.py b/nlightreader/contexts/ReadMark.py
index e4e82969..445412f2 100644
--- a/nlightreader/contexts/ReadMark.py
+++ b/nlightreader/contexts/ReadMark.py
@@ -1,6 +1,6 @@
from qfluentwidgets import Action, FluentIcon, RoundMenu
-from nlightreader.utils import translate
+from nlightreader.utils.translator import translate
class ReadMarkMenu(RoundMenu):
diff --git a/nlightreader/controlers/filter_controller.py b/nlightreader/controlers/filter_controller.py
index 87b83e26..48e5c40f 100644
--- a/nlightreader/controlers/filter_controller.py
+++ b/nlightreader/controlers/filter_controller.py
@@ -2,7 +2,7 @@
from qfluentwidgets import CheckBox, RadioButton
from nlightreader.dialogs import FormGenres
-from nlightreader.items import Genre, Kind, Order
+from nlightreader.models import Genre, Kind, Order
class FilterController:
@@ -16,17 +16,15 @@ def __init__(self):
self._kinds_container = None
self._genres_container = None
- def get_active_order(self):
- if not self._order_items:
- return Order.get_empty_instance()
+ def get_active_order(self) -> Order | None:
for item in self._order_items:
if item.isChecked():
return self._order_items[item]
- def get_active_kinds(self):
+ def get_active_kinds(self) -> list[Kind]:
return [self._kind_items[i] for i in self._kind_items if i.isChecked()]
- def get_active_genres(self):
+ def get_active_genres(self) -> list[Genre]:
return self._genres_container.selected_genres
def add_orders(self, items: list[Order]):
diff --git a/nlightreader/dialogs/Auth.py b/nlightreader/dialogs/Auth.py
index 048efbfd..8c37f430 100644
--- a/nlightreader/dialogs/Auth.py
+++ b/nlightreader/dialogs/Auth.py
@@ -8,7 +8,7 @@
SubtitleLabel,
)
-from nlightreader.utils import translate
+from nlightreader.utils.translator import translate
class AbstractAuthDialog(MessageBoxBase):
diff --git a/nlightreader/dialogs/Character.py b/nlightreader/dialogs/Character.py
index 7ad7abed..6ddb7dda 100644
--- a/nlightreader/dialogs/Character.py
+++ b/nlightreader/dialogs/Character.py
@@ -2,9 +2,10 @@
from PySide6.QtWidgets import QDialog
from data.ui.dialogs.character import Ui_Dialog
-from nlightreader.utils import description_to_html, Worker
-from nlightreader.utils.catalog_manager import get_catalog
+from nlightreader.utils.catalog_manager import get_catalog_by_id
from nlightreader.utils.file_manager import FileManager
+from nlightreader.utils.text_formatter import description_to_html
+from nlightreader.utils.threads import Worker
class FormCharacter(QDialog):
@@ -14,11 +15,11 @@ def __init__(self, character, catalog_id, parent=None):
self.ui.setupUi(self)
self.ui.show_spoilers.checkedChanged.connect(self.update_description)
self.setFixedSize(QSize(550, 800))
- self.character = character
- self.setWindowTitle(self.character.get_name())
- self.catalog = get_catalog(catalog_id)()
- self.ui.name_label.setText(self.character.name)
- self.ui.russian_label.setText(self.character.russian)
+ self.__character = character
+ self.setWindowTitle(self.__character.get_name())
+ self.__catalog = get_catalog_by_id(catalog_id)
+ self.ui.name_label.setText(self.__character.name)
+ self.ui.russian_label.setText(self.__character.russian)
self.update_description()
Worker(self.setup_image).start()
@@ -29,7 +30,7 @@ def closeEvent(self, arg__1):
def update_description(self):
self.ui.description.setHtml(
description_to_html(
- self.character.description,
+ self.__character.description,
self.ui.show_spoilers.isChecked(),
),
)
@@ -37,7 +38,7 @@ def update_description(self):
def setup_image(self):
self.ui.image.setPixmap(
FileManager.get_character_preview(
- self.character,
- self.catalog,
+ self.__character,
+ self.__catalog,
),
)
diff --git a/nlightreader/dialogs/Genres.py b/nlightreader/dialogs/Genres.py
index bce11e41..f1c8fb85 100644
--- a/nlightreader/dialogs/Genres.py
+++ b/nlightreader/dialogs/Genres.py
@@ -9,7 +9,7 @@ def __init__(self, parent=None):
super().__init__(parent)
self.ui_ge = Ui_Dialog()
self.ui_ge.setupUi(self)
- self.layout().setSizeConstraint(QLayout.SetFixedSize)
+ self.layout().setSizeConstraint(QLayout.SizeConstraint.SetFixedSize)
self.setWindowTitle("Genres")
self.ui_ge.ok_btn.clicked.connect(self.accept_genres)
self.ui_ge.cancel_btn.clicked.connect(self.reject_genres)
diff --git a/nlightreader/dialogs/Rate.py b/nlightreader/dialogs/Rate.py
index 6d0336b0..ef477b3b 100644
--- a/nlightreader/dialogs/Rate.py
+++ b/nlightreader/dialogs/Rate.py
@@ -4,9 +4,12 @@
from data.ui.dialogs.rate import Ui_Dialog
from nlightreader.consts.enums import LIB_LISTS, Nl
-from nlightreader.items import Manga
-from nlightreader.utils import translate
-from nlightreader.utils.catalog_manager import get_catalog, get_lib_catalog
+from nlightreader.models import Manga
+from nlightreader.utils.translator import translate
+from nlightreader.utils.catalog_manager import (
+ get_catalog_by_id,
+ get_lib_catalog,
+)
class FormRate(QDialog):
@@ -21,40 +24,52 @@ def __init__(self, manga: Manga, parent=None):
self.ui.lib_list_box.addItems(
[translate("Form", i.capitalize()) for i in LIB_LISTS],
)
- self.ui.update_btn.clicked.connect(self.send_rate)
+ self.ui.update_btn.clicked.connect(self.send_user_rate)
self.ui.cancel_btn.clicked.connect(self.close)
- self.ui.delete_btn.clicked.connect(self.delete_rate)
- self.manga = manga
- self.catalog = get_lib_catalog(get_catalog(self.manga.catalog_id))()
+ self.ui.delete_btn.clicked.connect(self.delete_user_rate)
- self.user_rate = None
+ self.__manga = manga
+ self.__catalog = get_lib_catalog(
+ get_catalog_by_id(
+ self.__manga.catalog_id,
+ ).__class__,
+ )
+ self.__user_rate = None
- self.setWindowTitle(f"{self.manga.get_name()}")
+ self.setWindowTitle(self.__manga.get_name())
self.setup()
def setup(self):
- if not self.catalog.check_user_rate(self.manga):
- self.catalog.create_user_rate(self.manga)
- self.user_rate = self.catalog.get_user_rate(self.manga)
- self.ui.score_box.setValue(self.user_rate.score)
- self.ui.chapters_box.setValue(self.user_rate.chapters)
- if self.manga.chapters:
- self.ui.chapters_box.setMaximum(self.manga.chapters)
- self.ui.lib_list_box.setCurrentIndex(self.user_rate.status.value)
+ self.fetch_user_rate()
+ self.display_user_rate()
def closeEvent(self, arg__1):
self.deleteLater()
+ def fetch_user_rate(self):
+ if not self.__catalog.check_user_rate(self.__manga):
+ self.__catalog.create_user_rate(self.__manga)
+ self.__user_rate = self.__catalog.get_user_rate(self.__manga)
+
+ def display_user_rate(self):
+ self.ui.score_box.setValue(self.__user_rate.score)
+ self.ui.chapters_box.setValue(self.__user_rate.chapters)
+ if self.__manga.chapters:
+ self.ui.chapters_box.setMaximum(self.__manga.chapters)
+ self.ui.lib_list_box.setCurrentIndex(self.__user_rate.status.value)
+
@Slot()
- def send_rate(self):
- self.user_rate.score = self.ui.score_box.value()
- self.user_rate.chapters = self.ui.chapters_box.value()
- self.user_rate.status = Nl.LibList(self.ui.lib_list_box.currentIndex())
- self.catalog.update_user_rate(self.user_rate)
+ def send_user_rate(self):
+ self.__user_rate.score = self.ui.score_box.value()
+ self.__user_rate.chapters = self.ui.chapters_box.value()
+ self.__user_rate.status = Nl.LibList(
+ self.ui.lib_list_box.currentIndex(),
+ )
+ self.__catalog.update_user_rate(self.__user_rate)
self.close()
@Slot()
- def delete_rate(self):
- self.catalog.delete_user_rate(self.user_rate)
+ def delete_user_rate(self):
+ self.__catalog.delete_user_rate(self.__user_rate)
self.close()
diff --git a/nlightreader/exceptions/__init__.py b/nlightreader/exceptions/__init__.py
new file mode 100644
index 00000000..ed34be5b
--- /dev/null
+++ b/nlightreader/exceptions/__init__.py
@@ -0,0 +1 @@
+from . import parser_content_exc
diff --git a/nlightreader/exceptions/parser_content_exc.py b/nlightreader/exceptions/parser_content_exc.py
new file mode 100644
index 00000000..462eea06
--- /dev/null
+++ b/nlightreader/exceptions/parser_content_exc.py
@@ -0,0 +1,10 @@
+class NoContentError(Exception):
+ pass
+
+
+class FetchContentError(Exception):
+ pass
+
+
+class RequestsParamsError(Exception):
+ pass
diff --git a/nlightreader/items/RequestForm.py b/nlightreader/items/RequestForm.py
index 113e0119..37105afa 100644
--- a/nlightreader/items/RequestForm.py
+++ b/nlightreader/items/RequestForm.py
@@ -1,5 +1,5 @@
from nlightreader.consts.enums import Nl
-from nlightreader.items.sort_items import Genre, Kind, Order
+from nlightreader.models.sort_models import Genre, Kind, Order
class RequestForm:
diff --git a/nlightreader/items/__init__.py b/nlightreader/items/__init__.py
index a5a0225f..66374ff6 100644
--- a/nlightreader/items/__init__.py
+++ b/nlightreader/items/__init__.py
@@ -1,4 +1,2 @@
from .RequestForm import RequestForm
-from .manga_items import Manga, Chapter, Image, Character
from .other_items import HistoryNote, User, UserRate
-from .sort_items import Kind, Genre, Order, Status
diff --git a/nlightreader/items/manga_items.py b/nlightreader/items/manga_items.py
deleted file mode 100644
index 5303505d..00000000
--- a/nlightreader/items/manga_items.py
+++ /dev/null
@@ -1,161 +0,0 @@
-import logging
-import re
-
-from PySide6.QtCore import QLocale
-
-from nlightreader.consts.enums import Nl
-from nlightreader.items.BaseItem import BaseItem
-from nlightreader.items.sort_items import Genre
-
-
-class Manga(BaseItem):
- def __init__(self, content_id: str, catalog_id, name, russian):
- super().__init__(content_id, catalog_id, name, russian)
- self.__kind: Nl.MangaKind = Nl.MangaKind.undefined
- self._description: dict[Nl.Language, str] = {}
- self.__score: int | float = 0
- self.status: str | None = None
- self.genres: list[Genre] = []
- self.volumes = 0
- self.chapters = 0
- self.preview_url: str | None = None
-
- @property
- def score(self):
- return self.__score
-
- @score.setter
- def score(self, score):
- if isinstance(score, float) and score.is_integer():
- score = int(score)
- self.__score = score
-
- @property
- def kind(self):
- return self.__kind
-
- @kind.setter
- def kind(self, kind):
- if not isinstance(kind, Nl.MangaKind):
- raise TypeError("Kind must be Nl.MangaKind")
- self.__kind = kind
-
- def add_description(self, language: Nl.Language, description: str):
- self._description.update({language: description})
-
- def get_description(self) -> str:
- if self._description.get(Nl.Language.undefined):
- return self._description.get(Nl.Language.undefined)
- if QLocale().language() in (
- QLocale.Language.Russian,
- QLocale.Language.Ukrainian,
- ) and self._description.get(Nl.Language.ru):
- return self._description.get(Nl.Language.ru)
- return self._description.get(Nl.Language.en)
-
- def descriptions_to_str(self) -> str:
- desc_str = ""
- for key in self._description:
- if self._description.get(key):
- desc_str += (
- f""
- f"{self._description.get(key)}"
- f""
- )
- return desc_str
-
- def set_description_from_str(self, desc: str):
- for lang, text in re.findall(
- r"(.+?)",
- desc,
- re.DOTALL,
- ):
- self.add_description(
- Nl.Language.from_str(lang),
- text,
- )
-
- def to_dict(self) -> dict:
- return {
- "id": self.id,
- "content_id": self.content_id,
- "catalog_id": self.catalog_id,
- "name": self.name,
- "russian": self.russian,
- "kind": self.kind.name,
- "description": self.descriptions_to_str(),
- "score": self.score,
- "status": self.status,
- "volumes": self.volumes,
- "chapters": self.chapters,
- "preview_url": self.preview_url,
- }
-
-
-class Chapter:
- def __init__(
- self,
- content_id: str,
- catalog_id: int,
- vol: str,
- ch: str,
- title: str,
- language: Nl.Language = Nl.Language.undefined,
- ):
- self.id = f"|{catalog_id}|_|{content_id}|"
- self.content_id = content_id
- self.catalog_id = catalog_id
- self.vol = vol
- self.ch = ch
- self.title = title
- self.__language = language
- self.translator = None
-
- def get_name(self) -> str:
- if not self.vol and not self.ch:
- return self.title
- if self.title:
- return f"{self.vol}-{self.ch} {self.title}"
- return f"{self.vol}-{self.ch}"
-
- @property
- def language(self):
- return self.__language
-
- def to_dict(self):
- return {
- "id": self.id,
- "content_id": self.content_id,
- "catalog_id": self.catalog_id,
- "vol": self.vol,
- "ch": self.ch,
- "title": self.title,
- "language": self.language.name,
- }
-
-
-class Image:
- def __init__(self, image_id: str, page: int, img: str):
- self.id = image_id
- self.page = page
- self.img = img
-
- @staticmethod
- def get_empty_instance():
- item_name = "image"
- return Image(item_name, 1, "")
-
-
-class Character(BaseItem):
- def __init__(
- self,
- content_id: str,
- catalog_id: int,
- name,
- russian,
- description,
- role,
- ):
- super().__init__(content_id, catalog_id, name, russian)
- self.description = description
- self.role = role
diff --git a/nlightreader/items/other_items.py b/nlightreader/items/other_items.py
index 45b0b39d..465d128d 100644
--- a/nlightreader/items/other_items.py
+++ b/nlightreader/items/other_items.py
@@ -1,5 +1,5 @@
from nlightreader.consts.enums import Nl
-from nlightreader.items import Chapter, Manga
+from nlightreader.models import Chapter, Manga
class HistoryNote:
diff --git a/nlightreader/items/sort_items.py b/nlightreader/items/sort_items.py
deleted file mode 100644
index 4fd5c5ef..00000000
--- a/nlightreader/items/sort_items.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from nlightreader.items.BaseItem import BaseItem
-
-
-class Kind(BaseItem):
- def __init__(self, content_id: str, catalog_id: int, name, russian):
- super().__init__(content_id, catalog_id, name, russian)
-
-
-class Order(BaseItem):
- def __init__(self, content_id: str, catalog_id: int, name, russian):
- super().__init__(content_id, catalog_id, name, russian)
-
-
-class Genre(BaseItem):
- def __init__(self, content_id: str, catalog_id: int, name, russian):
- super().__init__(content_id, catalog_id, name, russian)
-
-
-class Status(BaseItem):
- def __init__(self, content_id: str, catalog_id: int, name, russian):
- super().__init__(content_id, catalog_id, name, russian)
diff --git a/nlightreader/models/__init__.py b/nlightreader/models/__init__.py
new file mode 100644
index 00000000..7230aeab
--- /dev/null
+++ b/nlightreader/models/__init__.py
@@ -0,0 +1,5 @@
+from .manga_model import Manga
+from .chapter_model import Chapter
+from .character_model import Character
+from .sort_models import Order, Kind, Genre
+from .image_model import Image
diff --git a/nlightreader/items/BaseItem.py b/nlightreader/models/base_model.py
similarity index 56%
rename from nlightreader/items/BaseItem.py
rename to nlightreader/models/base_model.py
index 5efbd856..23716423 100644
--- a/nlightreader/items/BaseItem.py
+++ b/nlightreader/models/base_model.py
@@ -1,19 +1,13 @@
from PySide6.QtCore import QLocale
+from nlightreader.utils.config import cfg
-class BaseItem:
- def __init__(
- self,
- content_id: str,
- catalog_id: int,
- name: str,
- russian: str,
- ):
+
+class BaseModel:
+ def __init__(self, content_id: str, catalog_id: int):
self.__id = f"|{catalog_id}|_|{content_id}|"
self.__content_id = content_id
self.__catalog_id = catalog_id
- self.name = name
- self.russian = russian
def __eq__(self, other):
return other.id == self.id
@@ -33,19 +27,39 @@ def content_id(self):
def catalog_id(self):
return self.__catalog_id
+ def to_dict(self) -> dict:
+ raise NotImplementedError
+
+
+class NamedBaseModel(BaseModel):
+ def __init__(
+ self,
+ content_id: str,
+ catalog_id: int,
+ name: str,
+ russian: str,
+ ):
+ super().__init__(content_id, catalog_id)
+ self.__name = name
+ self.__russian = russian
+
+ @property
+ def name(self):
+ return self.__name
+
+ @property
+ def russian(self):
+ return self.__russian
+
def get_name(self) -> str:
+ locale = cfg.get(cfg.language).value.language()
if (
- QLocale().language()
+ locale
in (
QLocale.Language.Russian,
QLocale.Language.Ukrainian,
)
- and self.russian
+ and self.__russian
):
- return self.russian
- return self.name
-
- @staticmethod
- def get_empty_instance():
- item_name = "base_item"
- return BaseItem(item_name, -1, item_name, item_name)
+ return self.__russian
+ return self.__name
diff --git a/nlightreader/models/chapter_model.py b/nlightreader/models/chapter_model.py
new file mode 100644
index 00000000..49ae2745
--- /dev/null
+++ b/nlightreader/models/chapter_model.py
@@ -0,0 +1,66 @@
+from types import NoneType
+
+from nlightreader.consts.enums import Nl
+from nlightreader.models.base_model import BaseModel
+
+
+class Chapter(BaseModel):
+ def __init__(
+ self,
+ content_id: str,
+ catalog_id: int,
+ volume_number: str | None,
+ chapter_number: str | None,
+ title: str,
+ language: Nl.Language = Nl.Language.undefined,
+ ):
+ super().__init__(content_id, catalog_id)
+ self.__volume_number = volume_number
+ self.__chapter_number = chapter_number
+ self.__title = title
+ self.__language = language
+ self.__translator: str | None = None
+
+ @property
+ def volume_number(self):
+ return self.__volume_number
+
+ @property
+ def chapter_number(self):
+ return self.__chapter_number
+
+ @property
+ def language(self):
+ return self.__language
+
+ @property
+ def translator(self):
+ return self.__translator
+
+ @translator.setter
+ def translator(self, translator: str):
+ if not isinstance(translator, (str, NoneType)):
+ raise TypeError(
+ f"Translator must be a string or None got {type(translator)}",
+ )
+ self.__translator = translator
+
+ def get_name(self) -> str:
+ if not self.__volume_number and not self.__chapter_number:
+ return self.__title
+
+ vol_ch_name = f"{self.__volume_number}-{self.__chapter_number}"
+ if self.__title:
+ return f"{vol_ch_name} {self.__title}"
+ return vol_ch_name
+
+ def to_dict(self):
+ return {
+ "id": self.id,
+ "content_id": self.content_id,
+ "catalog_id": self.catalog_id,
+ "vol": self.__volume_number,
+ "ch": self.__chapter_number,
+ "title": self.__title,
+ "language": self.language.name,
+ }
diff --git a/nlightreader/models/character_model.py b/nlightreader/models/character_model.py
new file mode 100644
index 00000000..dc852fe0
--- /dev/null
+++ b/nlightreader/models/character_model.py
@@ -0,0 +1,38 @@
+from nlightreader.models.base_model import NamedBaseModel
+
+
+class Character(NamedBaseModel):
+ def __init__(
+ self,
+ content_id: str,
+ catalog_id: int,
+ name: str,
+ russian: str,
+ description: str,
+ role: str,
+ ):
+ super().__init__(content_id, catalog_id, name, russian)
+ self.description = description
+ self.role = role
+
+ @property
+ def description(self):
+ return self.__description
+
+ @description.setter
+ def description(self, description: str):
+ if not isinstance(description, str):
+ raise TypeError(
+ f"Description must be a string got {type(description)}",
+ )
+ self.__description = description
+
+ @property
+ def role(self):
+ return self.__role
+
+ @role.setter
+ def role(self, role: str):
+ if not isinstance(role, str):
+ raise TypeError(f"Role must be a string got {type(role)}")
+ self.__role = role
diff --git a/nlightreader/models/image_model.py b/nlightreader/models/image_model.py
new file mode 100644
index 00000000..b4148042
--- /dev/null
+++ b/nlightreader/models/image_model.py
@@ -0,0 +1,24 @@
+from types import NoneType
+
+import validators
+
+
+class Image:
+ def __init__(self, image_id: str, page_number: int, url: str | None):
+ self.id = image_id
+ self.page_number = page_number
+ self.__url = None
+
+ self.url = url
+
+ @property
+ def url(self):
+ return self.__url
+
+ @url.setter
+ def url(self, url: str | None):
+ if not isinstance(url, (str, NoneType)):
+ raise TypeError(f"Url must be str or None got {type(url)}")
+ if url is not None and not validators.url(url):
+ raise ValueError(f"Url {url} is not valid")
+ self.__url = url
diff --git a/nlightreader/models/manga_model.py b/nlightreader/models/manga_model.py
new file mode 100644
index 00000000..23da1117
--- /dev/null
+++ b/nlightreader/models/manga_model.py
@@ -0,0 +1,160 @@
+import re
+from types import NoneType
+from typing import override
+
+import validators
+from PySide6.QtCore import QLocale
+
+from nlightreader.consts.enums import Nl
+from nlightreader.models.base_model import NamedBaseModel
+from nlightreader.utils.config import cfg
+
+
+class Manga(NamedBaseModel):
+ def __init__(
+ self,
+ content_id: str,
+ catalog_id: int,
+ name: str,
+ russian: str,
+ ):
+ super().__init__(content_id, catalog_id, name, russian)
+
+ self.__kind: Nl.MangaKind = Nl.MangaKind.undefined
+ self.__status: Nl.MangaStatus = Nl.MangaStatus.undefined
+ self.__score: int | float = 0
+ self.__preview_url: str | None = None
+
+ self.__volumes = 0
+ self.__chapters = 0
+
+ self.__descriptions: dict[Nl.Language, str] = {}
+
+ @property
+ def kind(self):
+ return self.__kind
+
+ @kind.setter
+ def kind(self, kind):
+ if not isinstance(kind, Nl.MangaKind):
+ raise TypeError(f"Kind must be Nl.MangaKind got {type(kind)}")
+ self.__kind = kind
+
+ @property
+ def status(self):
+ return self.__status
+
+ @status.setter
+ def status(self, status: Nl.MangaStatus):
+ if not isinstance(status, Nl.MangaStatus):
+ raise TypeError(
+ f"Status must be Nl.MangaStatus got {type(status)}",
+ )
+ self.__status = status
+
+ @property
+ def score(self):
+ return self.__score
+
+ @score.setter
+ def score(self, score: int | float):
+ if not isinstance(score, (int, float)):
+ raise TypeError(f"Score must be int or float got {type(score)}")
+
+ if isinstance(score, float) and score.is_integer():
+ score = int(score)
+ self.__score = score
+
+ @property
+ def preview_url(self):
+ return self.__preview_url
+
+ @preview_url.setter
+ def preview_url(self, url: str | None):
+ if not isinstance(url, (str, NoneType)):
+ raise TypeError(f"Preview url must be str or None got {type(url)}")
+ if url is not None and not validators.url(url):
+ raise ValueError(f"Url {url} is not valid")
+ self.__preview_url = url
+
+ @property
+ def volumes(self):
+ return self.__volumes
+
+ @volumes.setter
+ def volumes(self, volumes: int):
+ if not isinstance(volumes, int):
+ raise TypeError(f"Volumes must be int got {type(volumes)}")
+ self.__volumes = volumes
+
+ @property
+ def chapters(self):
+ return self.__chapters
+
+ @chapters.setter
+ def chapters(self, chapters: int):
+ if not isinstance(chapters, int):
+ raise TypeError(f"Chapters must be int got {type(chapters)}")
+ self.__chapters = chapters
+
+ def add_description(self, language: Nl.Language, description: str):
+ if not isinstance(language, Nl.Language):
+ raise TypeError(
+ f"Language must be Nl.Language got {type(language)}",
+ )
+ if not isinstance(description, str):
+ raise TypeError(
+ f"Description must be str got {type(description)}",
+ )
+ self.__descriptions.update({language: description})
+
+ def get_description(self) -> str:
+ if self.__descriptions.get(Nl.Language.undefined):
+ return self.__descriptions.get(Nl.Language.undefined)
+
+ locale = cfg.get(cfg.language).value.language()
+ if locale in (
+ QLocale.Language.Russian,
+ QLocale.Language.Ukrainian,
+ ) and self.__descriptions.get(Nl.Language.ru):
+ return self.__descriptions.get(Nl.Language.ru)
+ return self.__descriptions.get(Nl.Language.en)
+
+ def descriptions_to_str(self) -> str:
+ desc_str = ""
+ for key in self.__descriptions:
+ if self.__descriptions.get(key):
+ desc_str += (
+ f""
+ f"{self.__descriptions.get(key)}"
+ f""
+ )
+ return desc_str
+
+ def set_description_from_str(self, desc: str):
+ for lang, text in re.findall(
+ r"(.+?)",
+ desc,
+ re.DOTALL,
+ ):
+ self.add_description(
+ Nl.Language.from_str(lang),
+ text,
+ )
+
+ @override
+ def to_dict(self) -> dict:
+ return {
+ "id": self.id,
+ "content_id": self.content_id,
+ "catalog_id": self.catalog_id,
+ "name": self.name,
+ "russian": self.russian,
+ "kind": self.kind.name,
+ "description": self.descriptions_to_str(),
+ "score": self.score,
+ "status": self.__status.name,
+ "volumes": self.__volumes,
+ "chapters": self.__chapters,
+ "preview_url": self.__preview_url,
+ }
diff --git a/nlightreader/models/sort_models.py b/nlightreader/models/sort_models.py
new file mode 100644
index 00000000..3f5d39b2
--- /dev/null
+++ b/nlightreader/models/sort_models.py
@@ -0,0 +1,45 @@
+from nlightreader.models.base_model import NamedBaseModel
+
+
+class Kind(NamedBaseModel):
+ def __init__(
+ self,
+ content_id: str,
+ catalog_id: int,
+ name: str,
+ russian: str,
+ ):
+ super().__init__(content_id, catalog_id, name, russian)
+
+
+class Order(NamedBaseModel):
+ def __init__(
+ self,
+ content_id: str,
+ catalog_id: int,
+ name: str,
+ russian: str,
+ ):
+ super().__init__(content_id, catalog_id, name, russian)
+
+
+class Genre(NamedBaseModel):
+ def __init__(
+ self,
+ content_id: str,
+ catalog_id: int,
+ name: str,
+ russian: str,
+ ):
+ super().__init__(content_id, catalog_id, name, russian)
+
+
+class Status(NamedBaseModel):
+ def __init__(
+ self,
+ content_id: str,
+ catalog_id: int,
+ name: str,
+ russian: str,
+ ):
+ super().__init__(content_id, catalog_id, name, russian)
diff --git a/nlightreader/parsers/LocalLib.py b/nlightreader/parsers/LocalLib.py
index eaa6d42e..9f8a8c33 100644
--- a/nlightreader/parsers/LocalLib.py
+++ b/nlightreader/parsers/LocalLib.py
@@ -1,4 +1,5 @@
-from nlightreader.items import Manga, RequestForm
+from nlightreader.items import RequestForm
+from nlightreader.models import Manga
from nlightreader.utils.database import Database
diff --git a/nlightreader/parsers/catalog.py b/nlightreader/parsers/catalog.py
index e1f0ac4d..af2f00d4 100644
--- a/nlightreader/parsers/catalog.py
+++ b/nlightreader/parsers/catalog.py
@@ -1,6 +1,11 @@
from nlightreader.consts.urls import DEFAULT_HEADERS
from nlightreader.consts.items.parser_items import ParserItems
from nlightreader.items import (
+ RequestForm,
+ User,
+ UserRate,
+)
+from nlightreader.models import (
Chapter,
Character,
Genre,
@@ -8,9 +13,6 @@
Kind,
Manga,
Order,
- RequestForm,
- User,
- UserRate,
)
diff --git a/nlightreader/parsers/combined/lib/lib_anilib.py b/nlightreader/parsers/combined/lib/lib_anilib.py
index f156c211..27500479 100644
--- a/nlightreader/parsers/combined/lib/lib_anilib.py
+++ b/nlightreader/parsers/combined/lib/lib_anilib.py
@@ -1,7 +1,7 @@
from nlightreader.consts.enums import Nl
from nlightreader.consts.items import AniLibItems
from nlightreader.consts.urls import URL_ANILIB
-from nlightreader.items import Chapter, Manga
+from nlightreader.models import Chapter, Manga
from nlightreader.parsers.catalogs_base import AbstractAnimeCatalog
from nlightreader.parsers.combined.lib.lib_base import LibBase
from nlightreader.utils.utils import get_html
@@ -28,7 +28,7 @@ def get_chapters(self, manga: Manga) -> list[Chapter]:
episode = Chapter(
i["id"],
self.CATALOG_ID,
- "",
+ None,
"",
f"Episode {i['number']}",
Nl.Language.ru,
diff --git a/nlightreader/parsers/combined/lib/lib_base.py b/nlightreader/parsers/combined/lib/lib_base.py
index f6844ef7..fddc222e 100644
--- a/nlightreader/parsers/combined/lib/lib_base.py
+++ b/nlightreader/parsers/combined/lib/lib_base.py
@@ -1,6 +1,7 @@
from nlightreader.consts.enums import Nl
from nlightreader.consts.urls import URL_LIB_API
-from nlightreader.items import Chapter, Manga, RequestForm
+from nlightreader.items import RequestForm
+from nlightreader.models import Chapter, Manga
from nlightreader.parsers.catalog import AbstractCatalog
from nlightreader.utils.utils import get_html
@@ -21,14 +22,13 @@ def get_manga(self, manga: Manga) -> Manga:
if response:
data = response["data"]
manga.preview_url = data["cover"]["md"]
- manga.score = data["rating"]["average"]
+ manga.score = float(data["rating"]["average"])
if description := data.get("summary"):
manga.add_description(Nl.Language.ru, description)
return manga
def search_manga(self, form: RequestForm) -> list[Manga]:
url = f"{self.url_api}/{self.content_name}"
- mangas = []
params = {
"site_id[]": self.site_id,
"sort_by": form.get_order_id(),
@@ -43,6 +43,8 @@ def search_manga(self, form: RequestForm) -> list[Manga]:
cookies=cookies,
content_type="json",
)
+
+ mangas = []
if response:
for i in response.get("data"):
manga = Manga(
@@ -52,7 +54,7 @@ def search_manga(self, form: RequestForm) -> list[Manga]:
i["rus_name"],
)
manga.preview_url = i["cover"]["md"]
- manga.score = i["rating"]["average"]
+ manga.score = float(i["rating"]["average"])
mangas.append(manga)
return mangas
diff --git a/nlightreader/parsers/combined/lib/lib_mangalib.py b/nlightreader/parsers/combined/lib/lib_mangalib.py
index f899e3fc..06a0daf2 100644
--- a/nlightreader/parsers/combined/lib/lib_mangalib.py
+++ b/nlightreader/parsers/combined/lib/lib_mangalib.py
@@ -1,6 +1,6 @@
from nlightreader.consts.items import MangaLibItems
from nlightreader.consts.urls import URL_MANGALIB
-from nlightreader.items import Chapter, Image, Manga
+from nlightreader.models import Chapter, Image, Manga
from nlightreader.parsers.catalogs_base import AbstractMangaCatalog
from nlightreader.parsers.combined.lib.lib_base import LibBase
from nlightreader.utils.utils import get_html
@@ -21,8 +21,8 @@ def __init__(self):
def get_images(self, manga: Manga, chapter: Chapter) -> list[Image]:
url = f"{self.url_api}/{self.content_name}/{manga.content_id}/chapter"
params = {
- "number": chapter.ch,
- "volume": chapter.vol,
+ "number": chapter.chapter_number,
+ "volume": chapter.volume_number,
}
response = get_html(url, params=params, content_type="json")
images = []
@@ -39,7 +39,7 @@ def get_images(self, manga: Manga, chapter: Chapter) -> list[Image]:
return images
def get_image(self, image: Image):
- return get_html(image.img, content_type="content")
+ return get_html(image.url, content_type="content")
def get_manga_url(self, manga: Manga) -> str:
return f"{self.url}/ru/manga/{manga.content_id}"
diff --git a/nlightreader/parsers/combined/lib/lib_ranobelib.py b/nlightreader/parsers/combined/lib/lib_ranobelib.py
index bb02f100..5508c160 100644
--- a/nlightreader/parsers/combined/lib/lib_ranobelib.py
+++ b/nlightreader/parsers/combined/lib/lib_ranobelib.py
@@ -4,7 +4,7 @@
from nlightreader.consts.enums import Nl
from nlightreader.consts.items import RanobeLibItems
from nlightreader.consts.urls import URL_RANOBELIB
-from nlightreader.items import Chapter, Image, Manga
+from nlightreader.models import Chapter, Image, Manga
from nlightreader.parsers.catalogs_base import AbstractRanobeCatalog
from nlightreader.parsers.combined.lib.lib_base import LibBase
from nlightreader.utils.utils import get_html
@@ -28,8 +28,9 @@ def get_manga(self, manga: Manga) -> Manga:
def get_images(self, manga: Manga, chapter: Chapter) -> list[Image]:
url = (
- f"{self.url_api}/{self.content_name}/{manga.content_id}/"
- f"chapter?number={chapter.ch}&volume={chapter.vol}"
+ f"{self.url_api}/{self.content_name}/{manga.content_id}/chapter"
+ f"?number={chapter.chapter_number}"
+ f"&volume={chapter.volume_number}"
)
return [Image("", 1, url)]
@@ -58,7 +59,7 @@ def replace_images(text: str):
text,
)
- response = get_html(image.img, content_type="json")
+ response = get_html(image.url, content_type="json")
if response:
data = response["data"]
content = data["content"]
diff --git a/nlightreader/parsers/combined/shikimori/shikimori_anime.py b/nlightreader/parsers/combined/shikimori/shikimori_anime.py
index c1da3ef7..546f68ed 100644
--- a/nlightreader/parsers/combined/shikimori/shikimori_anime.py
+++ b/nlightreader/parsers/combined/shikimori/shikimori_anime.py
@@ -6,13 +6,9 @@
URL_SHIKIMORI_API,
)
from nlightreader.items import (
- Chapter,
- Character,
- Genre,
- Manga,
- Order,
RequestForm,
)
+from nlightreader.models import Chapter, Character, Genre, Manga, Order
from nlightreader.parsers.catalogs_base import AbstractAnimeCatalog
from nlightreader.parsers.service.kodik import Kodik
from nlightreader.utils.utils import get_html
@@ -43,11 +39,13 @@ def get_manga(self, manga: Manga) -> Manga:
data = response
# manga.kind = Nl.MangaKind.from_str(data.get("kind"))
manga.score = float(data.get("score"))
- manga.status = data.get("status")
- manga.add_description(
- Nl.Language.undefined,
- data.get("description"),
- )
+ manga.status = Nl.MangaStatus.from_str(data.get("status"))
+
+ if description := data.get("description"):
+ manga.add_description(
+ Nl.Language.undefined,
+ description,
+ )
return manga
def search_manga(self, form: RequestForm):
@@ -66,6 +64,7 @@ def search_manga(self, form: RequestForm):
params=params,
content_type="json",
)
+
mangas = []
if response:
for i in response:
@@ -80,7 +79,7 @@ def get_chapters(self, manga: Manga) -> list[Chapter]:
chapter = Chapter(
f"{translator.content_id}{episode_num}",
self.CATALOG_ID,
- "",
+ None,
"",
f"Episode {episode_num}",
Nl.Language.ru,
@@ -99,7 +98,8 @@ def get_character(self, character: Character) -> Character:
url = f"{self.url_api}/characters/{character.content_id}"
response = get_html(url, headers=self.headers, content_type="json")
if response:
- character.description = response.get("description")
+ if description := response.get("description"):
+ character.description = description
return character
def get_preview(self, manga: Manga):
diff --git a/nlightreader/parsers/combined/shikimori/shikimori_base.py b/nlightreader/parsers/combined/shikimori/shikimori_base.py
index 9f586e59..f69a1d07 100644
--- a/nlightreader/parsers/combined/shikimori/shikimori_base.py
+++ b/nlightreader/parsers/combined/shikimori/shikimori_base.py
@@ -5,7 +5,7 @@
URL_SHIKIMORI,
URL_SHIKIMORI_API,
)
-from nlightreader.items import Character, Genre, Manga, Order
+from nlightreader.models import Character, Genre, Manga, Order
from nlightreader.parsers.catalog import AbstractCatalog
from nlightreader.utils.utils import get_html
@@ -36,23 +36,25 @@ def get_manga(self, manga: Manga) -> Manga:
data = response
manga.kind = Nl.MangaKind.from_str(data.get("kind"))
manga.score = float(data.get("score"))
- manga.status = data.get("status")
+ manga.status = Nl.MangaStatus.from_str(data.get("status"))
if data.get("volumes"):
manga.volumes = int(data.get("volumes"))
if data.get("chapters"):
manga.chapters = int(data.get("chapters"))
- manga.add_description(
- Nl.Language.undefined,
- data.get("description"),
- )
+ if description := data.get("description"):
+ manga.add_description(
+ Nl.Language.undefined,
+ description,
+ )
return manga
def get_character(self, character: Character) -> Character:
url = f"{self.url_api}/characters/{character.content_id}"
response = get_html(url, headers=self.headers, content_type="json")
if response:
- character.description = response.get("description")
+ if description := response.get("description"):
+ character.description = description
return character
def get_preview(self, manga: Manga):
@@ -71,18 +73,19 @@ def get_character_preview(self, character: Character):
def get_genres(self):
url = f"{self.url_api}/genres"
response = get_html(url, headers=self.headers, content_type="json")
- if response:
- return [
- Genre(
- str(i["id"]),
- self.CATALOG_ID,
- i["name"],
- i["russian"],
- )
- for i in response
- if i["entry_type"] == "Manga"
- ]
- return []
+ if not response:
+ return []
+
+ return [
+ Genre(
+ str(i["id"]),
+ self.CATALOG_ID,
+ i["name"],
+ i["russian"],
+ )
+ for i in response
+ if i["entry_type"] == "Manga"
+ ]
def get_orders(self) -> list[Order]:
return [
@@ -101,33 +104,35 @@ def get_relations(self, manga: Manga) -> list[Manga]:
response = get_html(url, headers=self.headers, content_type="json")
if response:
for i in response:
- if i.get("manga"):
- i = i.get("manga")
- mangas.append(self.setup_manga(i))
+ if manga_data := i.get("manga"):
+ mangas.append(
+ self.setup_manga(manga_data),
+ )
return mangas
def get_characters(self, manga: Manga) -> list[Character]:
characters = []
url = f"{self.url_api}/mangas/{manga.content_id}/roles"
response = get_html(url, headers=self.headers, content_type="json")
- if response:
- for i in response:
- if i.get("roles"):
- role = i.get("roles")[0]
- if role in ["Supporting", "Main"]:
- data = i.get("character")
- if data:
- characters.append(
- Character(
- str(data.get("id")),
- self.CATALOG_ID,
- data.get("name"),
- data.get("russian"),
- "",
- role,
- ),
- )
- characters.sort(key=lambda x: x.role)
+ if not response:
+ return characters
+
+ for i in response:
+ if roles_data := i.get("roles"):
+ role = roles_data[0]
+ if role in ["Supporting", "Main"]:
+ if data := i.get("character"):
+ characters.append(
+ Character(
+ str(data.get("id")),
+ self.CATALOG_ID,
+ data.get("name"),
+ data.get("russian"),
+ "",
+ role,
+ ),
+ )
+ characters.sort(key=lambda x: x.role)
return characters
def get_manga_url(self, manga: Manga) -> str:
diff --git a/nlightreader/parsers/combined/shikimori/shikimori_lib.py b/nlightreader/parsers/combined/shikimori/shikimori_lib.py
index 9bf8c4d1..77e366d0 100644
--- a/nlightreader/parsers/combined/shikimori/shikimori_lib.py
+++ b/nlightreader/parsers/combined/shikimori/shikimori_lib.py
@@ -11,7 +11,8 @@
URL_SHIKIMORI_TOKEN,
)
from nlightreader.consts.enums import Nl
-from nlightreader.items import Manga, RequestForm, User, UserRate
+from nlightreader.items import RequestForm, User, UserRate
+from nlightreader.models import Manga
from nlightreader.parsers.catalog import LibParser
from nlightreader.parsers.combined.shikimori.shikimori_base import (
ShikimoriBase,
@@ -36,7 +37,6 @@ def search_manga(self, form: RequestForm):
url = f"{self.url_api}/users/{self.session.user.id}/manga_rates"
params = {"limit": 50, "page": form.page}
response = self.session.request("GET", url, params=params)
- mangas = []
lib_list = form.lib_list
if lib_list == Nl.LibList.reading:
lib_list = "watching"
@@ -44,6 +44,8 @@ def search_manga(self, form: RequestForm):
lib_list = "rewatching"
else:
lib_list = form.lib_list.name
+
+ mangas = []
if response and (resp_json := response.json()):
for i in resp_json:
if not i.get("status") == lib_list:
@@ -132,7 +134,7 @@ def update_user_rate(self, user_rate: UserRate):
@singleton
class Auth:
- def __init__(self, token=None, scope=None):
+ def __init__(self):
self.client_id = SHIKIMORI_CLIENT_ID
self.client_secret = SHIKIMORI_CLIENT_SECRET
self.redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
@@ -144,7 +146,7 @@ def __init__(self, token=None, scope=None):
self.headers = SHIKIMORI_HEADERS | {
"Authorization": f"Bearer {self.tokens.get('access_token')}",
}
- self.client = self.get_client(scope, self.redirect_uri, token)
+ self.client = self.get_client("user_rates", self.redirect_uri, None)
self.refresh_token()
self.user: User = User(None, None, None)
self.is_authorized = False
@@ -155,7 +157,7 @@ def auth_login(self, params):
self.fetch_token(params["token"])
self.check_auth()
- def get_client(self, scope, redirect_uri, token):
+ def get_client(self, scope, redirect_uri, token: dict | None):
client = OAuth2Session(
self.client_id,
auto_refresh_url=URL_SHIKIMORI_TOKEN,
@@ -231,11 +233,12 @@ def request(
ignore_authorize=False,
):
if (
- (not ignore_authorize and not self.is_authorized)
- or "test" in QApplication.arguments()
+ "test" in QApplication.arguments()
or "noshiki" in QApplication.arguments()
):
- return
+ return None
+ if not ignore_authorize and not self.is_authorized:
+ return None
try:
response = self.client.request(
method,
@@ -255,10 +258,10 @@ def request(
f"\t\tJson: {json}\n",
)
- def check_auth(self):
+ def check_auth(self) -> bool:
url = f"{URL_SHIKIMORI_API}/users/whoami"
whoami = self.request("GET", url, ignore_authorize=True)
- self.is_authorized = whoami and whoami.json()
+ self.is_authorized = bool(whoami) and bool(whoami.json())
return self.is_authorized
@property
diff --git a/nlightreader/parsers/combined/shikimori/shikimori_manga.py b/nlightreader/parsers/combined/shikimori/shikimori_manga.py
index faa7ed1f..974756fb 100644
--- a/nlightreader/parsers/combined/shikimori/shikimori_manga.py
+++ b/nlightreader/parsers/combined/shikimori/shikimori_manga.py
@@ -1,5 +1,6 @@
from nlightreader.consts.items import ShikimoriItems
-from nlightreader.items import Kind, RequestForm
+from nlightreader.items import RequestForm
+from nlightreader.models import Kind
from nlightreader.parsers.catalogs_base import AbstractMangaCatalog
from nlightreader.parsers.combined.shikimori.shikimori_base import (
ShikimoriBase,
@@ -26,6 +27,7 @@ def search_manga(self, form: RequestForm):
params=params,
content_type="json",
)
+
mangas = []
if response:
for i in response:
diff --git a/nlightreader/parsers/combined/shikimori/shikimori_ranobe.py b/nlightreader/parsers/combined/shikimori/shikimori_ranobe.py
index f017aae2..1e7a8336 100644
--- a/nlightreader/parsers/combined/shikimori/shikimori_ranobe.py
+++ b/nlightreader/parsers/combined/shikimori/shikimori_ranobe.py
@@ -25,6 +25,7 @@ def search_manga(self, form: RequestForm):
params=params,
content_type="json",
)
+
mangas = []
if response:
for i in response:
diff --git a/nlightreader/parsers/hentai_manga/allhentai_hmanga.py b/nlightreader/parsers/hentai_manga/allhentai_hmanga.py
index 264b0bf6..ba9292a7 100644
--- a/nlightreader/parsers/hentai_manga/allhentai_hmanga.py
+++ b/nlightreader/parsers/hentai_manga/allhentai_hmanga.py
@@ -2,7 +2,8 @@
from nlightreader.consts.urls import URL_ALLHENTAI, URL_ALLHENTAI_API
from nlightreader.consts.enums import Nl
-from nlightreader.items import Chapter, Image, Manga
+from nlightreader.exceptions import parser_content_exc
+from nlightreader.models import Chapter, Image, Manga
from nlightreader.parsers.catalogs_base import AbstractHentaiMangaCatalog
from nlightreader.utils.utils import get_html, make_request
@@ -18,7 +19,15 @@ def __init__(self):
def search_manga(self, form):
url = f"{self.url}/search"
- params = {"q": form.search, "+": "Искать!", "fast-filter": "CREATION"}
+ if not form.search:
+ raise parser_content_exc.RequestsParamsError(
+ "Search field is empty",
+ )
+ params = {
+ "q": form.search,
+ "+": "Искать!",
+ "fast-filter": "CREATION",
+ }
response = make_request(
url,
"POST",
@@ -36,37 +45,42 @@ def search_manga(self, form):
manga_id = base_info.get("href")
name = base_info.get("title")
if manga_id and name:
- mangas.append(Manga(manga_id, self.CATALOG_ID, name, ""))
+ mangas.append(
+ Manga(manga_id, self.CATALOG_ID, name, ""),
+ )
return mangas
def get_chapters(self, manga: Manga):
url = f"{self.url}/{manga.content_id}"
response = get_html(url, headers=self.headers, content_type="text")
+
chapters = []
- if response:
- soup = BeautifulSoup(response, "html.parser")
- chapters_list_item = soup.find("div", id="chapters-list")
- for chapter_item in chapters_list_item.findAll(
- "tr",
- class_="item-row",
- ):
- volume: str = chapter_item.get("data-vol")
- chapter_num: str = chapter_item.get("data-num")
- if chapter_num.isdigit():
- chapter_as_num = int(chapter_num) / 10
- if chapter_as_num.is_integer():
- chapter_as_num = int(chapter_as_num)
- chapter_num = str(chapter_as_num)
+ if not response:
+ return chapters
+
+ soup = BeautifulSoup(response, "html.parser")
+ chapters_list_item = soup.find("div", id="chapters-list")
+ for chapter_item in chapters_list_item.findAll(
+ "tr",
+ class_="item-row",
+ ):
+ volume: str = chapter_item.get("data-vol")
+ chapter_num: str = chapter_item.get("data-num")
+ if chapter_num.isdigit():
+ chapter_as_num = int(chapter_num) / 10
+ if chapter_as_num.is_integer():
+ chapter_as_num = int(chapter_as_num)
+ chapter_num = str(chapter_as_num)
- chapter = Chapter(
- manga.content_id,
- self.CATALOG_ID,
- volume,
- chapter_num,
- "",
- Nl.Language.ru,
- )
- chapters.append(chapter)
+ chapter = Chapter(
+ manga.content_id,
+ self.CATALOG_ID,
+ volume,
+ chapter_num,
+ "",
+ Nl.Language.ru,
+ )
+ chapters.append(chapter)
return chapters
def get_images(self, manga: Manga, chapter: Chapter):
diff --git a/nlightreader/parsers/hentai_manga/nhentai_hmanga.py b/nlightreader/parsers/hentai_manga/nhentai_hmanga.py
index b471c774..7b14eb40 100644
--- a/nlightreader/parsers/hentai_manga/nhentai_hmanga.py
+++ b/nlightreader/parsers/hentai_manga/nhentai_hmanga.py
@@ -1,7 +1,9 @@
-from bs4 import BeautifulSoup
+import validators
+from bs4 import BeautifulSoup, element
-from nlightreader.consts.urls import URL_NHENTAI, URL_NHENTAI_API
-from nlightreader.items import Chapter, Image, Manga
+from nlightreader.consts.urls import URL_NHENTAI
+from nlightreader.exceptions import parser_content_exc
+from nlightreader.models import Chapter, Image, Manga
from nlightreader.parsers.catalogs_base import AbstractHentaiMangaCatalog
from nlightreader.utils.utils import get_html
@@ -13,38 +15,54 @@ class NHentai(AbstractHentaiMangaCatalog):
def __init__(self):
super().__init__()
self.url = URL_NHENTAI
- self.url_api = URL_NHENTAI_API
def search_manga(self, form):
url = f"{self.url}/search"
- params = {"page": form.page, "q": form.search}
+ if not form.search:
+ raise parser_content_exc.RequestsParamsError(
+ "Search field is empty",
+ )
+ params = {
+ "page": form.page,
+ "q": form.search,
+ }
response = get_html(
url,
headers=self.headers,
params=params,
content_type="text",
)
+
mangas = []
- if response:
- soup = BeautifulSoup(response, "html.parser")
- html_items = soup.findAll("div", class_="gallery")
- for i in html_items:
- caption_tag = i.find("div", class_="caption")
- if caption_tag is not None:
- name = i.find("div", class_="caption").text
- cover_tag = i.find("a", {"class": "cover"})
- if cover_tag is not None:
- manga_id = cover_tag["href"].split("/")[-2]
- if not manga_id:
- continue
- mangas.append(
- Manga(
- manga_id,
- self.CATALOG_ID,
- name,
- "",
- ),
- )
+ if not response:
+ return mangas
+
+ soup = BeautifulSoup(response, "html.parser")
+ html_items = soup.findAll("div", class_="gallery")
+ for i in html_items:
+ caption_tag = i.find("div", class_="caption")
+ if caption_tag is not None:
+ name = i.find("div", class_="caption").text
+ cover_tag: element.Tag = i.find("a", {"class": "cover"})
+ if cover_tag is not None:
+ manga_id = cover_tag["href"].split("/")[-2]
+ if not manga_id:
+ continue
+
+ manga = Manga(
+ manga_id,
+ self.CATALOG_ID,
+ name,
+ "",
+ )
+
+ if (noscript_img_tag := cover_tag.find("noscript")) and (
+ img_tag := noscript_img_tag.find("img")
+ ):
+ src = img_tag.get("src")
+ if validators.url(src):
+ manga.preview_url = src
+ mangas.append(manga)
return mangas
def get_chapters(self, manga: Manga):
@@ -68,14 +86,8 @@ def get_images(self, manga: Manga, chapter: Chapter):
for i in html_items:
img_tag = i.find("img", class_="")
img_url: str = img_tag["src"]
- for img_format in ["png", "jpg", "gif"]:
- if img_url.endswith(f"t.{img_format}"):
- img_url = img_url.replace(
- f"t.{img_format}",
- f".{img_format}",
- 1,
- )
- break
+ if not validators.url(img_url):
+ continue
images.append(Image("", html_items.index(i) + 1, img_url))
return images
@@ -84,30 +96,17 @@ def get_image(self, image: Image):
"Referer": URL_NHENTAI,
}
return get_html(
- image.img,
+ image.url,
headers=img_request_headers,
content_type="content",
)
def get_preview(self, manga: Manga):
- url = f"{self.url}/g/{manga.content_id}"
- response = get_html(
- url,
+ return get_html(
+ manga.preview_url,
headers=self.headers,
- content_type="text",
+ content_type="content",
)
- if response:
- soup = BeautifulSoup(response, "html.parser")
- if html_item := soup.find("div", id="cover"):
- if img_tag := html_item.find("img"):
- img_request_headers = self.headers | {
- "Referer": URL_NHENTAI,
- }
- return get_html(
- img_tag["src"],
- content_type="content",
- headers=img_request_headers,
- )
def get_manga_url(self, manga: Manga) -> str:
return f"{self.url}/g/{manga.content_id}"
diff --git a/nlightreader/parsers/manga/Lib.py b/nlightreader/parsers/manga/Lib.py
index 7fc8098f..b67fb7d7 100644
--- a/nlightreader/parsers/manga/Lib.py
+++ b/nlightreader/parsers/manga/Lib.py
@@ -1,6 +1,6 @@
from nlightreader.consts.items import MangaLibItems
from nlightreader.consts.urls import URL_SLASHLIB
-from nlightreader.items import Manga
+from nlightreader.models import Manga
from nlightreader.parsers.catalogs_base import AbstractMangaCatalog
from nlightreader.utils.utils import get_html
diff --git a/nlightreader/parsers/manga/desu_manga.py b/nlightreader/parsers/manga/desu_manga.py
index e6a00189..b5762be1 100644
--- a/nlightreader/parsers/manga/desu_manga.py
+++ b/nlightreader/parsers/manga/desu_manga.py
@@ -1,7 +1,8 @@
from nlightreader.consts.urls import DESU_HEADERS, URL_DESU, URL_DESU_API
from nlightreader.consts.enums import Nl
from nlightreader.consts.items import DesuItems
-from nlightreader.items import Chapter, Genre, Image, Manga, RequestForm
+from nlightreader.items import RequestForm
+from nlightreader.models import Chapter, Image, Manga
from nlightreader.parsers.catalogs_base import AbstractMangaCatalog
from nlightreader.utils.utils import get_data, get_html
@@ -22,20 +23,11 @@ def get_manga(self, manga: Manga):
response = get_html(url, headers=self.headers, content_type="json")
if response:
data = get_data(response, ["response"], {})
- manga.genres = [
- Genre(
- str(i.get("id")),
- self.CATALOG_ID,
- i.get("text"),
- i.get("russian"),
- )
- for i in data.get("genres")
- ]
manga.score = data.get("score")
manga.kind = Nl.MangaKind.from_str(data.get("kind"))
- manga.volumes = data.get("chapters").get("last").get("vol")
- manga.chapters = data.get("chapters").get("last").get("ch")
- manga.status = data.get("status")
+ manga.volumes = int(data["chapters"].get("last").get("vol"))
+ manga.chapters = int(data["chapters"]["count"])
+ manga.status = Nl.MangaStatus.from_str(data.get("status"))
manga.add_description(
Nl.Language.undefined,
@@ -59,18 +51,21 @@ def search_manga(self, form: RequestForm):
params=params,
content_type="json",
)
- manga = []
- if response:
- for i in get_data(response, ["response"]):
- manga.append(
- Manga(
- str(i.get("id")),
- self.CATALOG_ID,
- i.get("name"),
- i.get("russian"),
- ),
- )
- return manga
+
+ mangas = []
+ if not response:
+ return mangas
+
+ for i in get_data(response, ["response"]):
+ mangas.append(
+ Manga(
+ str(i.get("id")),
+ self.CATALOG_ID,
+ i.get("name"),
+ i.get("russian"),
+ ),
+ )
+ return mangas
def get_chapters(self, manga: Manga):
url = f"{self.url_api}/{manga.content_id}"
@@ -108,7 +103,7 @@ def get_images(self, manga: Manga, chapter: Chapter):
def get_image(self, image: Image):
headers = self.headers | {"Referer": f"{self.url}/"}
- return get_html(image.img, headers=headers, content_type="content")
+ return get_html(image.url, headers=headers, content_type="content")
def get_preview(self, manga: Manga):
return get_html(
diff --git a/nlightreader/parsers/manga/mangadex_manga.py b/nlightreader/parsers/manga/mangadex_manga.py
index 05dec378..fe6e19c3 100644
--- a/nlightreader/parsers/manga/mangadex_manga.py
+++ b/nlightreader/parsers/manga/mangadex_manga.py
@@ -8,14 +8,10 @@
)
from nlightreader.consts.enums import Nl
from nlightreader.items import (
- Chapter,
- Genre,
- Image,
- Kind,
- Manga,
RequestForm,
User,
)
+from nlightreader.models import Chapter, Genre, Image, Kind, Manga
from nlightreader.parsers.catalog import LibParser
from nlightreader.parsers.catalogs_base import AbstractMangaCatalog
from nlightreader.utils.decorators import singleton
@@ -45,8 +41,7 @@ def get_manga(self, manga: Manga) -> Manga:
if response:
data = get_data(response, ["data"])
manga.kind = Nl.MangaKind.from_str(data.get("type"))
- description = get_data(data, ["attributes", "description"])
- if description:
+ if description := get_data(data, ["attributes", "description"]):
if description.get("en"):
manga.add_description(
Nl.Language.en,
@@ -57,13 +52,13 @@ def get_manga(self, manga: Manga) -> Manga:
Nl.Language.ru,
description.get("ru"),
)
- volumes = get_data(data, ["attributes", "lastVolume"])
- chapters = get_data(data, ["attributes", "lastChapter"])
- if volumes:
- manga.volumes = volumes
- if chapters:
- manga.chapters = chapters
- manga.status = get_data(data, ["attributes", "status"])
+ if volumes := get_data(data, ["attributes", "lastVolume"]):
+ manga.volumes = int(volumes)
+ if chapters := get_data(data, ["attributes", "lastChapter"]):
+ manga.chapters = int(chapters)
+ manga.status = Nl.MangaStatus.from_str(
+ get_data(data, ["attributes", "status"]),
+ )
return manga
def setup_manga(self, data: dict):
@@ -92,16 +87,19 @@ def search_manga(self, form: RequestForm):
"pornographic",
],
}
- mangas = []
response = get_html(
url,
headers=self.headers,
params=params,
content_type="json",
)
- if response:
- for i in get_data(response, ["data"]):
- mangas.append(self.setup_manga(i))
+
+ mangas = []
+ if not response:
+ return mangas
+
+ for i in get_data(response, ["data"]):
+ mangas.append(self.setup_manga(i))
return mangas
def get_chapters(self, manga: Manga):
@@ -162,7 +160,7 @@ def get_images(self, manga: Manga, chapter: Chapter):
def get_image(self, image: Image):
return get_html(
- image.img,
+ image.url,
headers=self.headers,
content_type="content",
)
@@ -189,10 +187,14 @@ def get_preview(self, manga: Manga):
def get_genres(self):
url = f"{self.url_api}/manga/tag"
- html = get_html(url, headers=self.headers)
+ response = get_html(
+ url,
+ headers=self.headers,
+ content_type="json",
+ )
genres = []
- if html and html.status_code == 200 and html.json():
- for i in html.json().get("data"):
+ if response:
+ for i in response.get("data"):
if i.get("attributes").get("group") not in ["genre", "theme"]:
continue
genres.append(
@@ -332,8 +334,9 @@ def auth_login(self, params):
self.refresh_token()
def get(self, url, params=None):
- if self.is_authorized:
- return get_html(url, params=params, headers=self.headers)
+ if not self.is_authorized:
+ return None
+ return get_html(url, params=params, headers=self.headers)
@property
def headers(self):
diff --git a/nlightreader/parsers/manga/remanga_manga.py b/nlightreader/parsers/manga/remanga_manga.py
index 1c16c39a..f64c4f6b 100644
--- a/nlightreader/parsers/manga/remanga_manga.py
+++ b/nlightreader/parsers/manga/remanga_manga.py
@@ -1,7 +1,8 @@
from nlightreader.consts.urls import URL_REMANGA, URL_REMANGA_API
from nlightreader.consts.enums import Nl
from nlightreader.consts.items import RemangaItems
-from nlightreader.items import Chapter, Image, Manga, RequestForm
+from nlightreader.items import RequestForm
+from nlightreader.models import Chapter, Image, Manga
from nlightreader.parsers.catalogs_base import AbstractMangaCatalog
from nlightreader.utils.utils import get_data, get_html
@@ -21,6 +22,14 @@ def get_manga(self, manga: Manga) -> Manga:
response = get_html(url, headers=self.headers, content_type="json")
if response:
data = response.get("content")
+
+ if kind_name := data.get("type").get("name"):
+ manga.kind = Nl.MangaKind.from_str(kind_name)
+
+ manga.score = float(data.get("avg_rating"))
+ if (img := data.get("img").get("high")) and (img != "/media/None"):
+ manga.preview_url = f"{self.url}{img}"
+
manga.add_description(
Nl.Language.undefined,
data.get("description"),
@@ -36,27 +45,30 @@ def search_manga(self, form: RequestForm):
"query": form.search,
"count": 40,
"ordering": form.get_order_id(),
+ "types": form.get_kind_ids(),
}
- params = list(params.items())
- [params.append(("types", kind_id)) for kind_id in form.get_kind_ids()]
response = get_html(
url,
headers=self.headers,
params=params,
content_type="json",
)
+
mangas = []
- if response:
- for i in response.get("content"):
- manga_id = i.get("dir")
- name = i.get("en_name")
- russian = i.get("rus_name")
- kind = Nl.MangaKind.from_str(i.get("type"))
- manga = Manga(manga_id, self.CATALOG_ID, name, russian)
- manga.kind = kind
- manga.score = i.get("avg_rating")
- manga.preview_url = i.get("img").get("high")
- mangas.append(manga)
+ if not response:
+ return mangas
+
+ for data in response.get("content"):
+ manga_id = data.get("dir")
+ name = data.get("en_name")
+ russian = data.get("rus_name")
+ manga = Manga(manga_id, self.CATALOG_ID, name, russian)
+ manga.kind = Nl.MangaKind.from_str(data.get("type"))
+ manga.score = float(data.get("avg_rating"))
+
+ if (img := data.get("img").get("high")) and (img != "/media/None"):
+ manga.preview_url = f"{self.url}{img}"
+ mangas.append(manga)
return mangas
def get_chapters(self, manga: Manga) -> list[Chapter]:
@@ -111,21 +123,20 @@ def get_images(self, manga: Manga, chapter: Chapter):
def get_image(self, image: Image):
headers = {
"User-Agent": "Nlight",
- "Referer": "https://remanga.org/",
+ "Referer": f"{self.url}/",
}
return get_html(
- f"{image.img}",
+ f"{image.url}",
headers=headers,
content_type="content",
)
def get_preview(self, manga: Manga):
- if manga.preview_url and manga.preview_url != "/media/None":
- return get_html(
- f"{self.url}{manga.preview_url}",
- headers=self.headers,
- content_type="content",
- )
+ return get_html(
+ manga.preview_url,
+ headers=self.headers,
+ content_type="content",
+ )
def get_manga_url(self, manga: Manga) -> str:
return f"{self.url}/manga/{manga.content_id}"
diff --git a/nlightreader/parsers/ranobe/ranobehub_ranobe.py b/nlightreader/parsers/ranobe/ranobehub_ranobe.py
index d05a6980..d134cba0 100644
--- a/nlightreader/parsers/ranobe/ranobehub_ranobe.py
+++ b/nlightreader/parsers/ranobe/ranobehub_ranobe.py
@@ -6,7 +6,8 @@
from nlightreader.consts.urls import URL_RANOBEHUB, URL_RANOBEHUB_API
from nlightreader.consts.enums import Nl
from nlightreader.consts.items import RanobehubItems
-from nlightreader.items import Chapter, Image, Manga, RequestForm
+from nlightreader.items import RequestForm
+from nlightreader.models import Chapter, Image, Manga
from nlightreader.parsers.catalogs_base import AbstractRanobeCatalog
from nlightreader.utils.utils import get_data, get_html
@@ -26,8 +27,12 @@ def get_manga(self, manga: Manga) -> Manga:
response = get_html(url, headers=self.headers, content_type="json")
if response:
data = response.get("data")
- manga.kind = Nl.MangaKind.ranobe
+
manga.score = data.get("rating")
+ manga.kind = Nl.MangaKind.ranobe
+
+ if status_name := data.get("status").get("title"):
+ manga.status = Nl.MangaStatus.from_str(status_name)
manga.add_description(
Nl.Language.undefined,
@@ -49,15 +54,21 @@ def search_manga(self, form: RequestForm):
params=params,
content_type="json",
)
+
mangas = []
- if response:
- for i in get_data(response, ["resource"], default_val=[]):
- manga_id = str(i.get("id"))
- name = i.get("names").get("eng")
- russian = i.get("names").get("rus")
- manga = Manga(manga_id, self.CATALOG_ID, name, russian)
- manga.preview_url = i.get("poster").get("medium")
- mangas.append(manga)
+ if not response:
+ return mangas
+
+ for i in get_data(response, ["resource"], default_val=[]):
+ manga_id = str(i.get("id"))
+ name = i.get("names").get("eng")
+ russian = i.get("names").get("rus")
+
+ manga = Manga(manga_id, self.CATALOG_ID, name, russian)
+ manga.status = Nl.MangaStatus.from_str(i.get("status"))
+ manga.preview_url = i.get("poster").get("medium")
+
+ mangas.append(manga)
return mangas
def get_chapters(self, manga: Manga) -> list[Chapter]:
@@ -82,8 +93,8 @@ def get_chapters(self, manga: Manga) -> list[Chapter]:
def get_images(self, manga: Manga, chapter: Chapter) -> list[Image]:
url = (
- f"{self.url}/ranobe/"
- f"{manga.content_id}/{chapter.vol}/{chapter.ch}"
+ f"{self.url}/ranobe/{manga.content_id}/"
+ f"{chapter.volume_number}/{chapter.chapter_number}"
)
return [Image("", 1, url)]
@@ -103,14 +114,14 @@ def find_text_container(
return container
# Parse HTML content and extract text container
- response = get_html(image.img, content_type="text")
+ response = get_html(image.url, content_type="text")
if response:
soup = BeautifulSoup(response, "html.parser")
text_container = find_text_container(
soup.findAll("div", {"class": "ui text container"}),
)
if not text_container:
- return
+ return None
# Construct content with images
content = ""
diff --git a/nlightreader/parsers/ranobe/rulate_ranobe.py b/nlightreader/parsers/ranobe/rulate_ranobe.py
index bd56bf9e..16e576e0 100644
--- a/nlightreader/parsers/ranobe/rulate_ranobe.py
+++ b/nlightreader/parsers/ranobe/rulate_ranobe.py
@@ -5,7 +5,8 @@
from nlightreader.consts.urls import URL_EROLATE, URL_RULATE
from nlightreader.consts.enums import Nl
from nlightreader.consts.items import RulateItems
-from nlightreader.items import Chapter, Image, Manga, RequestForm
+from nlightreader.items import RequestForm
+from nlightreader.models import Chapter, Image, Manga
from nlightreader.parsers.catalogs_base import AbstractRanobeCatalog
from nlightreader.utils.utils import get_html
@@ -42,7 +43,6 @@ def get_manga(self, manga: Manga) -> Manga:
return manga
def search_manga(self, form: RequestForm):
- ranobe = []
params = {
"t": form.search,
"cat": 12,
@@ -55,30 +55,34 @@ def search_manga(self, form: RequestForm):
params=params,
content_type="text",
)
- if response:
- soup = BeautifulSoup(response, "html.parser")
- hranobe = soup.findAll("p", class_="book-tooltip")
- for i in hranobe:
- name_text = i.text.strip()
- name_items = name_text.split("/")
- name = name_text
- russian = ""
- if len(name_items) == 2:
- name = name_items[0].strip()
- russian = name_items[1].strip()
- ranobe_id = str(
- i.unwrap()["data-tooltip-content"].split(
- "#book-tooltip-",
- )[-1],
- )
- ranobe.append(
- Manga(
- ranobe_id,
- self.CATALOG_ID,
- name,
- russian,
- ),
- )
+
+ ranobe = []
+ if not response:
+ return ranobe
+
+ soup = BeautifulSoup(response, "html.parser")
+ hranobe = soup.findAll("p", class_="book-tooltip")
+ for i in hranobe:
+ name_text = i.text.strip()
+ name_items = name_text.split("/")
+ name = name_text
+ russian = ""
+ if len(name_items) == 2:
+ name = name_items[0].strip()
+ russian = name_items[1].strip()
+ ranobe_id = str(
+ i.unwrap()["data-tooltip-content"].split(
+ "#book-tooltip-",
+ )[-1],
+ )
+ ranobe.append(
+ Manga(
+ ranobe_id,
+ self.CATALOG_ID,
+ name,
+ russian,
+ ),
+ )
return ranobe
def get_chapters(self, manga: Manga):
@@ -104,7 +108,7 @@ def get_chapters(self, manga: Manga):
chapter = Chapter(
chapter_id,
self.CATALOG_ID,
- "",
+ None,
"",
name,
Nl.Language.ru,
@@ -136,7 +140,7 @@ def get_chapter_content_image(media_id: str):
# Parse HTML content and extract text container
response = get_html(
- image.img,
+ image.url,
cookies=self.cookies,
content_type="text",
)
@@ -144,7 +148,7 @@ def get_chapter_content_image(media_id: str):
soup = BeautifulSoup(response, "html.parser")
text_container = soup.find("div", class_="content-text")
if not text_container:
- return
+ return None
# Construct content with images
content = ""
diff --git a/nlightreader/utils/__init__.py b/nlightreader/utils/__init__.py
deleted file mode 100644
index 696f126e..00000000
--- a/nlightreader/utils/__init__.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from .catalog_manager import (
- CATALOGS,
- USER_CATALOGS,
- LIB_CATALOGS,
- get_catalog,
- get_lib_catalog,
-)
-from .database import Database
-from .file_manager import FileManager
-from .text_formatter import description_to_html, translate, get_status
-from .threads import Worker, Thread
-from .token import TokenManager
-from .utils import *
diff --git a/nlightreader/utils/catalog_manager.py b/nlightreader/utils/catalog_manager.py
index d5365e2c..536ad1a4 100644
--- a/nlightreader/utils/catalog_manager.py
+++ b/nlightreader/utils/catalog_manager.py
@@ -1,3 +1,5 @@
+import logging
+
from nlightreader.parsers import (
AllHentai,
Desu,
@@ -18,7 +20,7 @@
ShikimoriRanobe,
SlashLib,
)
-
+from nlightreader.parsers.catalog import AbstractCatalog
CATALOGS = {
0: Desu,
@@ -56,9 +58,12 @@
LIB_CATALOGS = {ShikimoriBase: ShikimoriLib, MangaDex: MangaDexLib}
-def get_catalog(catalog_id=0):
- return CATALOGS.get(catalog_id)
+def get_catalog_by_id(catalog_id):
+ if catalog_id not in CATALOGS:
+ logging.warning(f"Catalog with id {catalog_id} not found.")
+ return AbstractCatalog()
+ return CATALOGS[catalog_id]()
def get_lib_catalog(base_catalog):
- return LIB_CATALOGS.get(base_catalog)
+ return LIB_CATALOGS.get(base_catalog)()
diff --git a/nlightreader/utils/config.py b/nlightreader/utils/config.py
new file mode 100644
index 00000000..6e7035f8
--- /dev/null
+++ b/nlightreader/utils/config.py
@@ -0,0 +1,84 @@
+from enum import Enum
+
+from PySide6.QtCore import QLocale
+from qfluentwidgets import (
+ BoolValidator,
+ ConfigItem,
+ ConfigSerializer,
+ OptionsConfigItem,
+ OptionsValidator,
+ QConfig,
+ qconfig,
+)
+
+from nlightreader.consts.paths.paths import APP_DATA_PATH
+
+
+class Language(Enum):
+ RUSSIAN = QLocale(QLocale.Language.Russian)
+ UKRAINIAN = QLocale(QLocale.Language.Ukrainian)
+ ENGLISH = QLocale(QLocale.Language.English)
+ AUTO = QLocale()
+
+
+class LanguageSerializer(ConfigSerializer):
+ def serialize(self, language):
+ return language.value.name() if language != Language.AUTO else "Auto"
+
+ def deserialize(self, value: str):
+ return Language(QLocale(value)) if value != "Auto" else Language.AUTO
+
+
+class Config(QConfig):
+ theme_mode = OptionsConfigItem(
+ "MainWindow",
+ "ThemeMode",
+ "Auto",
+ OptionsValidator(
+ [
+ "Light",
+ "Dark",
+ "Auto",
+ ],
+ ),
+ )
+ dpi_scale = OptionsConfigItem(
+ "MainWindow",
+ "DpiScale",
+ "Auto",
+ OptionsValidator(
+ [
+ 1,
+ 1.25,
+ 1.5,
+ 1.75,
+ 2,
+ "Auto",
+ ],
+ ),
+ restart=True,
+ )
+ language = OptionsConfigItem(
+ "MainWindow",
+ "Language",
+ Language.ENGLISH,
+ OptionsValidator(Language),
+ LanguageSerializer(),
+ restart=True,
+ )
+ check_updates_at_startup = ConfigItem(
+ "Update",
+ "CheckUpdateAtStartUp",
+ True,
+ BoolValidator(),
+ )
+ enable_kodik_server = ConfigItem(
+ "Utils",
+ "EnableKodikServer",
+ True,
+ BoolValidator(),
+ )
+
+
+cfg = Config()
+qconfig.load(APP_DATA_PATH / "config.json", cfg)
diff --git a/nlightreader/utils/database.py b/nlightreader/utils/database.py
index b0335a4f..3f6fe20d 100644
--- a/nlightreader/utils/database.py
+++ b/nlightreader/utils/database.py
@@ -1,17 +1,19 @@
+import logging
+
import sqlalchemy
-import platformdirs
from sqlalchemy.dialects.sqlite import insert
-from nlightreader.consts.app import APP_NAME
from nlightreader.consts.enums import Nl
-from nlightreader.items import Chapter, HistoryNote, Manga
+from nlightreader.consts.paths import APP_DATA_PATH
+from nlightreader.items import HistoryNote
+from nlightreader.models import Chapter, Manga
from nlightreader.utils.decorators import singleton
@singleton
class Database:
def __init__(self):
- db_file_path = f"{platformdirs.user_data_dir()}/{APP_NAME}/data.db"
+ db_file_path = APP_DATA_PATH / "data.db"
self.__engine = sqlalchemy.create_engine(f"sqlite:///{db_file_path}")
self._metadata = sqlalchemy.MetaData()
@@ -216,9 +218,14 @@ def _make_manga(manga_data) -> Manga:
manga.kind = Nl.MangaKind.from_str(manga_data[5])
manga.set_description_from_str(manga_data[6])
manga.score = manga_data[7]
- manga.status = manga_data[8]
+ manga.status = Nl.MangaStatus.from_str(manga_data[8])
manga.volumes = manga_data[9]
- manga.chapters = manga_data[10]
+
+ chapters = manga_data[10]
+ if not isinstance(chapters, int):
+ logging.warning(f"Chapters must be int got {type(chapters)}")
+ chapters = 0
+ manga.chapters = chapters
manga.preview_url = manga_data[11]
return manga
@@ -256,21 +263,19 @@ def add_chapters(self, chapters: list[Chapter], manga: Manga):
conn.execute(chapters_insert)
conn.commit()
- def get_chapter(self, chapter_id: str):
- select_chapter = sqlalchemy.select(
- self._chapters,
- ).where(
- self._chapters.c.id == chapter_id,
- )
- with self.__engine.connect() as conn:
- select_chapter_result = conn.execute(select_chapter)
- a = select_chapter_result.fetchone()
- content_id = str(a[1])
- catalog_id = a[2]
- vol = a[3]
- ch = a[4]
- title = a[5]
- language = Nl.Language.from_str(a[6])
+ @staticmethod
+ def __make_chapter(chapter_data) -> Chapter:
+ content_id = str(chapter_data[1])
+ catalog_id = chapter_data[2]
+
+ vol_raw = chapter_data[3]
+ vol = str(vol_raw) if vol_raw is not None else vol_raw
+
+ ch_raw = chapter_data[4]
+ ch = str(ch_raw) if ch_raw is not None else ch_raw
+
+ title = chapter_data[5]
+ language = Nl.Language.from_str(chapter_data[6])
return Chapter(
content_id,
@@ -281,6 +286,17 @@ def get_chapter(self, chapter_id: str):
language,
)
+ def get_chapter(self, chapter_id: str):
+ select_chapter = sqlalchemy.select(
+ self._chapters,
+ ).where(
+ self._chapters.c.id == chapter_id,
+ )
+ with self.__engine.connect() as conn:
+ select_chapter_result = conn.execute(select_chapter)
+ a = select_chapter_result.fetchone()
+ return self.__make_chapter(a)
+
def get_chapters(self, manga: Manga) -> list[Chapter]:
select_chapters = sqlalchemy.select(self._chapters).filter_by(
manga_id=manga.id,
@@ -291,14 +307,7 @@ def get_chapters(self, manga: Manga) -> list[Chapter]:
chapters = []
for i in a[::-1]:
chapters.append(
- Chapter(
- i[1],
- i[2],
- i[3],
- i[4],
- i[5],
- Nl.Language.from_str(a[6]),
- ),
+ self.__make_chapter(i),
)
return chapters
diff --git a/nlightreader/utils/file_manager.py b/nlightreader/utils/file_manager.py
index d49c05a4..7cc4997a 100644
--- a/nlightreader/utils/file_manager.py
+++ b/nlightreader/utils/file_manager.py
@@ -3,12 +3,11 @@
import shutil
from pathlib import Path
-import platformdirs
from PySide6.QtGui import QPixmap
-from nlightreader.consts.app import APP_NAME
from nlightreader.consts.enums import Nl
-from nlightreader.items import Chapter, Character, Image, Manga
+from nlightreader.consts.paths import APP_DATA_PATH
+from nlightreader.models import Chapter, Character, Image, Manga
from nlightreader.parsers.catalog import AbstractCatalog
@@ -66,9 +65,9 @@ def check_image_exists(
image: Image,
catalog: AbstractCatalog,
) -> bool:
- file_name = f"{image.page}.jpg"
+ file_name = f"{image.page_number}.jpg"
if manga.kind == Nl.MangaKind.ranobe:
- file_name = f"{image.page}.txt"
+ file_name = f"{image.page_number}.txt"
return check_file_exists(
cls.__get_chapter_folder(manga, chapter, catalog),
file_name,
@@ -83,7 +82,7 @@ def get_image_file(
catalog: AbstractCatalog,
):
path = cls.__get_chapter_folder(manga, chapter, catalog)
- file_name = f"{image.page}.jpg"
+ file_name = f"{image.page_number}.jpg"
if not check_file_exists(path, file_name):
save_file(path, file_name, catalog.get_image(image))
return QPixmap(get_full_file_path(path, file_name))
@@ -97,7 +96,7 @@ def get_chapter_text_file(
catalog: AbstractCatalog,
) -> str:
path = cls.__get_chapter_folder(manga, chapter, catalog)
- file_name = f"{image.page}.txt"
+ file_name = f"{image.page_number}.txt"
if not check_file_exists(path, file_name):
save_file(path, file_name, catalog.get_image(image))
try:
@@ -160,7 +159,7 @@ def open_dir_in_explorer(cls, manga: Manga, catalog: AbstractCatalog):
def get_full_dir_path(path: Path) -> Path:
path = fix_path(path)
- return platformdirs.user_data_path() / APP_NAME / path
+ return APP_DATA_PATH / path
def get_full_file_path(path: Path, file_name: str | Path) -> Path:
diff --git a/nlightreader/utils/html_video.py b/nlightreader/utils/html_video.py
index 17ff51f3..9a7f5b5b 100644
--- a/nlightreader/utils/html_video.py
+++ b/nlightreader/utils/html_video.py
@@ -1,6 +1,6 @@
from render_html import render_in_browser
-from nlightreader.items import Chapter, Manga
+from nlightreader.models import Chapter, Manga
def start_html_video(manga: Manga, chapter: Chapter):
diff --git a/nlightreader/utils/kodik_server.py b/nlightreader/utils/kodik_server.py
index 41ab3429..d1088e66 100644
--- a/nlightreader/utils/kodik_server.py
+++ b/nlightreader/utils/kodik_server.py
@@ -2,7 +2,7 @@
from http.server import BaseHTTPRequestHandler
from nlightreader.items import HistoryNote
-from nlightreader.utils import Database
+from nlightreader.utils.database import Database
class KodikHTTPRequestHandler(BaseHTTPRequestHandler):
diff --git a/nlightreader/utils/text_formatter.py b/nlightreader/utils/text_formatter.py
index 7c671310..5ed36dd6 100644
--- a/nlightreader/utils/text_formatter.py
+++ b/nlightreader/utils/text_formatter.py
@@ -1,7 +1,5 @@
import re
-from PySide6.QtWidgets import QApplication
-
class TextFormatter:
"""
@@ -97,40 +95,3 @@ def description_to_html(text: str, show_spoilers=False) -> str:
if not text:
return ""
return TextFormatter(text, show_spoilers).to_html_text()
-
-
-def translate(context, string):
- """
- Translates a string using the current translation context.
-
- Args:
- context: The context in which the string appears.
- string: The string to be translated.
-
- Returns:
- A translated version of the input string.
- """
- return QApplication.translate(context, string, None)
-
-
-def get_status(status: str) -> str:
- """
- Translates the status of a manga.
-
- Args:
- status (str or None): The status of the manga.
-
- Returns:
- A translation of the status if it is
- 'ongoing', 'completed', or 'released'.
- Otherwise, returns the original status.
- """
- if status is None:
- return ""
- match status:
- case "ongoing":
- return translate("Status", status.capitalize())
- case "completed" | "released":
- return translate("Status", "completed".capitalize())
- case _:
- return status.capitalize()
diff --git a/nlightreader/utils/threads.py b/nlightreader/utils/threads.py
index 9d5cae89..ff76b6b6 100644
--- a/nlightreader/utils/threads.py
+++ b/nlightreader/utils/threads.py
@@ -1,3 +1,4 @@
+import traceback
from typing import Callable
from PySide6.QtCore import (
@@ -11,6 +12,7 @@
class Signals(QObject):
+ error = Signal(Exception)
finished = Signal(object)
@@ -22,6 +24,7 @@ def __init__(
kwargs=None,
*,
callback=None,
+ error_callback=None,
):
super().__init__()
if kwargs is None:
@@ -32,11 +35,18 @@ def __init__(
self.signals = Signals()
if callback:
self.signals.finished.connect(callback)
+ if error_callback:
+ self.signals.error.connect(error_callback)
@Slot()
def run(self):
- result = self._target(*self._args, **self._kwargs)
- self.signals.finished.emit(result)
+ try:
+ result = self._target(*self._args, **self._kwargs)
+ except Exception as e:
+ traceback.print_exc()
+ self.signals.error.emit(e)
+ else:
+ self.signals.finished.emit(result)
class Worker(NlThread, QRunnable):
@@ -66,8 +76,15 @@ def __init__(
kwargs=None,
*,
callback=None,
+ error_callback=None,
):
- super().__init__(target, args, kwargs, callback=callback)
+ super().__init__(
+ target,
+ args,
+ kwargs,
+ callback=callback,
+ error_callback=error_callback,
+ )
def start(self, pool=None):
if pool is None:
@@ -104,5 +121,12 @@ def __init__(
kwargs=None,
*,
callback=None,
+ error_callback=None,
):
- super().__init__(target, args, kwargs, callback=callback)
+ super().__init__(
+ target,
+ args,
+ kwargs,
+ callback=callback,
+ error_callback=error_callback,
+ )
diff --git a/nlightreader/utils/translator.py b/nlightreader/utils/translator.py
new file mode 100644
index 00000000..b44ead6f
--- /dev/null
+++ b/nlightreader/utils/translator.py
@@ -0,0 +1,25 @@
+from PySide6.QtCore import QLocale, QTranslator
+from PySide6.QtWidgets import QApplication
+
+
+class NlightTranslator(QTranslator):
+ def __init__(self, locale: QLocale = None, parent=None):
+ super().__init__(parent=parent)
+ self.load(locale or QLocale())
+
+ def load(self, locale: QLocale):
+ super().load(f":/translations/i18n/{locale.name()}.qm")
+
+
+def translate(context, string):
+ """
+ Translates a string using the current translation context.
+
+ Args:
+ context: The context in which the string appears.
+ string: The string to be translated.
+
+ Returns:
+ A translated version of the input string.
+ """
+ return QApplication.translate(context, string, None)
diff --git a/nlightreader/utils/utils.py b/nlightreader/utils/utils.py
index 2424daaa..68dc38da 100644
--- a/nlightreader/utils/utils.py
+++ b/nlightreader/utils/utils.py
@@ -2,12 +2,11 @@
from typing import Any
import requests
-from PySide6.QtCore import QLocale
from PySide6.QtWidgets import QApplication
from nlightreader.consts.urls import DEFAULT_HEADERS
from nlightreader.consts.enums import Nl
-from nlightreader.consts.files import LangIcons, Translations
+from nlightreader.consts.files import LangIcons
def make_request(
@@ -49,7 +48,7 @@ def make_request(
Returns None if there was an error.
"""
if "test" in QApplication.arguments():
- return
+ return None
if headers is None:
headers = DEFAULT_HEADERS
try:
@@ -151,26 +150,6 @@ def get_language_icon(language: Nl.Language) -> str:
return lang_icons.get(language)
-def get_locale(locale: QLocale.Language) -> str:
- """
- Returns the translation file path for the specified locale.
-
- :param locale:
- A QLocale.Language object
- representing the target language.
- :return:
- The file path to the translation
- file associated with the locale as a string,
- or the default translation file if no
- matching translation is found.
- """
- translations = {
- QLocale.Language.Russian: Translations.Ru,
- QLocale.Language.Ukrainian: Translations.Uk,
- }
- return translations.get(locale, Translations.En)
-
-
def get_data(data: dict, path: list, default_val=None) -> Any:
"""
Retrieves a value from a dictionary using a list of nested keys.
diff --git a/nlightreader/widgets/NlightContainers/content_container.py b/nlightreader/widgets/NlightContainers/content_container.py
index b1eddc2c..94e07493 100644
--- a/nlightreader/widgets/NlightContainers/content_container.py
+++ b/nlightreader/widgets/NlightContainers/content_container.py
@@ -1,13 +1,100 @@
+from enum import Enum, unique
+
from PySide6.QtWidgets import QWidget
+from qfluentwidgets import (
+ FluentIcon,
+ IndeterminateProgressRing,
+ TransparentPushButton,
+)
+
+from nlightreader.utils.translator import translate
+
+
+@unique
+class ContentContainerState(Enum):
+ empty = 0
+ show_content = 1
+ fetch_content = 2
+ no_content = 3
+ fetch_error = 4
class AbstractContentContainer:
+ def __init__(self):
+ self._progress_ring = IndeterminateProgressRing()
+ self._progress_ring.setVisible(False)
+
+ self._fetch_error_widget = TransparentPushButton(
+ FluentIcon.CLOUD,
+ translate("Message", "No connection"),
+ )
+ self._fetch_error_widget.setEnabled(False)
+ self._fetch_error_widget.setVisible(False)
+
+ self._no_content_error_widget = TransparentPushButton(
+ FluentIcon.CLOUD,
+ translate("Message", "Nothing found"),
+ )
+ self._no_content_error_widget.setEnabled(False)
+ self._no_content_error_widget.setVisible(False)
+
+ self._content_widget = None
+
+ self._state = ContentContainerState.empty
+
def install(self, parent):
+ self.get_content_widget().layout().addWidget(
+ self._no_content_error_widget,
+ )
+ self.get_content_widget().layout().addWidget(
+ self._fetch_error_widget,
+ )
+ self.get_content_widget().layout().addWidget(
+ self._progress_ring,
+ )
parent.addWidget(self)
def _reset_area(self) -> None:
raise NotImplementedError
+ def set_state(self, state: ContentContainerState):
+ state_objects = (
+ self._progress_ring,
+ self._no_content_error_widget,
+ self._fetch_error_widget,
+ self._content_widget,
+ )
+ if not isinstance(state, ContentContainerState):
+ raise TypeError(
+ f"state must be ContentContainerState got {type(state)}",
+ )
+
+ self._state = state
+
+ state_obj = None
+ if state == ContentContainerState.empty:
+ state_obj = None
+
+ elif state == ContentContainerState.show_content:
+ state_obj = self._content_widget
+
+ elif state == ContentContainerState.fetch_content:
+ state_obj = self._progress_ring
+
+ elif state == ContentContainerState.no_content:
+ state_obj = self._no_content_error_widget
+
+ elif state == ContentContainerState.fetch_error:
+ state_obj = self._fetch_error_widget
+
+ [
+ obj.setVisible(
+ obj == state_obj,
+ )
+ for obj in state_objects
+ if obj is not None
+ ]
+
def set_content(self, content) -> None:
raise NotImplementedError
diff --git a/nlightreader/widgets/NlightContainers/image_area.py b/nlightreader/widgets/NlightContainers/image_area.py
index 6d1f5196..4bec6b74 100644
--- a/nlightreader/widgets/NlightContainers/image_area.py
+++ b/nlightreader/widgets/NlightContainers/image_area.py
@@ -19,6 +19,7 @@ def __init__(self):
QScrollArea {border: none;}
""",
)
+ self._content_widget = self.ui.img_lbl
self.__image_pixmap = None
def resizeEvent(self, event):
@@ -73,4 +74,4 @@ def set_content(self, img_pixmap: QPixmap):
self.__update_image()
def get_content_widget(self):
- return self.ui.img_lbl
+ return self.ui.img_lbl.parent()
diff --git a/nlightreader/widgets/NlightContainers/manga_area.py b/nlightreader/widgets/NlightContainers/manga_area.py
index 1585bd36..8add8746 100644
--- a/nlightreader/widgets/NlightContainers/manga_area.py
+++ b/nlightreader/widgets/NlightContainers/manga_area.py
@@ -1,10 +1,13 @@
from PySide6.QtCore import Qt, QThreadPool
from PySide6.QtWidgets import QGridLayout, QHBoxLayout, QWidget
-from qfluentwidgets import ScrollArea
+from qfluentwidgets import (
+ ScrollArea,
+)
-from nlightreader.utils import Thread
+from nlightreader.utils.threads import Thread
from nlightreader.widgets.NlightContainers.content_container import (
AbstractContentContainer,
+ ContentContainerState,
)
from nlightreader.widgets.NlightWidgets.manga_item import MangaItem
@@ -49,9 +52,12 @@ def resizeEvent(self, arg__1):
super().resizeEvent(arg__1)
if arg__1.oldSize().width() != arg__1.size().width():
self._scrollAreaWidgetContents.setFixedWidth(arg__1.size().width())
- self.update_items()
+ if self._state == ContentContainerState.show_content:
+ self.update_items()
def add_items(self, items: list[MangaItem]):
+ if self._state != ContentContainerState.show_content:
+ raise PermissionError("this method is now available in this state")
i, j = 0, 0
for item in items:
self._manga_items.append(item)
@@ -85,6 +91,8 @@ def delete_items(self):
self._manga_items.clear()
def update_items(self):
+ if self._state != ContentContainerState.show_content:
+ raise PermissionError("this method is now available in this state")
size = (
self.size().width()
- (self._content_grid.horizontalSpacing() * self._column_count)
diff --git a/nlightreader/widgets/NlightContainers/text_area.py b/nlightreader/widgets/NlightContainers/text_area.py
index 7d0bf961..ea86d018 100644
--- a/nlightreader/widgets/NlightContainers/text_area.py
+++ b/nlightreader/widgets/NlightContainers/text_area.py
@@ -13,6 +13,7 @@ def __init__(self):
self.ui = Ui_Form()
self.ui.setupUi(self)
self.ui.size_slider.valueChanged.connect(self.__update_text_size)
+ self._content_widget = self.ui.text_browser
@Slot()
def __update_text_size(self):
@@ -28,4 +29,4 @@ def set_content(self, content: str):
self.ui.text_browser.setHtml(content)
def get_content_widget(self) -> QWidget:
- return self.ui.text_browser
+ return self.ui.text_browser.parent()
diff --git a/nlightreader/widgets/NlightTemplates/BaseWidget.py b/nlightreader/widgets/NlightTemplates/BaseWidget.py
index bdae74a3..0cfabcd0 100644
--- a/nlightreader/widgets/NlightTemplates/BaseWidget.py
+++ b/nlightreader/widgets/NlightTemplates/BaseWidget.py
@@ -2,11 +2,19 @@
from PySide6.QtCore import Signal, Slot
from PySide6.QtWidgets import QWidget
-from qfluentwidgets import IndeterminateProgressRing
from nlightreader.consts.enums import Nl
-from nlightreader.items import Manga, RequestForm
-from nlightreader.utils import Thread
+from nlightreader.exceptions.parser_content_exc import (
+ FetchContentError,
+ NoContentError,
+ RequestsParamsError,
+)
+from nlightreader.items import RequestForm
+from nlightreader.models import Manga
+from nlightreader.utils.threads import Thread
+from nlightreader.widgets.NlightContainers.content_container import (
+ ContentContainerState,
+)
from nlightreader.widgets.NlightContainers.manga_area import MangaArea
from nlightreader.widgets.NlightWidgets.manga_item import MangaItem
@@ -19,12 +27,10 @@ def __init__(self, parent=None):
self.manga_area = MangaArea()
self.mangas: list[Manga] = []
- self.progressRing = IndeterminateProgressRing()
- self.progressRing.setVisible(False)
-
self._get_content_thread = Thread(
target=self._get_content_thread_func,
callback=self.update_content,
+ error_callback=self.__process_errors,
)
self.catalog = None
@@ -35,12 +41,19 @@ def setup(self):
def update_content(self):
self.manga_area.delete_items()
- items = [self.setup_manga_item(manga) for manga in self.mangas]
- self.progressRing.stop()
- self.progressRing.setVisible(False)
+ items = [self._setup_manga_item(manga) for manga in self.mangas]
+ self.manga_area.set_state(ContentContainerState.show_content)
self.manga_area.add_items(items)
self.manga_area.update_items()
+ def __process_errors(self, exception: Exception):
+ try:
+ raise exception
+ except FetchContentError:
+ self.manga_area.set_state(ContentContainerState.fetch_error)
+ except (NoContentError, RequestsParamsError):
+ self.manga_area.set_state(ContentContainerState.no_content)
+
@Slot()
def turn_page_next(self):
if self.request_params.page == 999:
@@ -60,8 +73,7 @@ def get_content(self):
self._get_content_thread.terminate()
self._get_content_thread.wait()
self.manga_area.delete_items()
- self.progressRing.setVisible(True)
- self.progressRing.start()
+ self.manga_area.set_state(ContentContainerState.fetch_content)
self._get_content_thread.start()
def _get_content_thread_func(self):
@@ -74,9 +86,11 @@ def _get_content_thread_func(self):
):
return
self.mangas = self.catalog.search_manga(self.request_params)
+ if not self.mangas:
+ raise NoContentError
- def setup_manga_item(self, manga: Manga) -> MangaItem:
- pass
+ def _setup_manga_item(self, manga: Manga) -> MangaItem:
+ raise NotImplementedError
def update_page(self):
pass
diff --git a/nlightreader/widgets/NlightTemplates/Facial.py b/nlightreader/widgets/NlightTemplates/Facial.py
index 10fef109..697327cf 100644
--- a/nlightreader/widgets/NlightTemplates/Facial.py
+++ b/nlightreader/widgets/NlightTemplates/Facial.py
@@ -1,11 +1,14 @@
+from typing import override
+
from PySide6.QtCore import Slot
from qfluentwidgets import FluentIcon
from data.ui.widgets.facial import Ui_Form
from nlightreader.controlers import FilterController
from nlightreader.dialogs import FormGenres
-from nlightreader.items import Manga
-from nlightreader.utils import translate, USER_CATALOGS
+from nlightreader.models import Manga
+from nlightreader.utils.catalog_manager import USER_CATALOGS
+from nlightreader.utils.translator import translate
from nlightreader.widgets.NlightTemplates.BaseWidget import (
MangaItemBasedWidget,
)
@@ -25,13 +28,6 @@ def __init__(self, parent=None):
self.setObjectName("FormFacial")
self.manga_area.install(self.ui.items_layout)
- (
- self.manga_area.get_content_widget()
- .layout()
- .addWidget(
- self.progressRing,
- )
- )
self.ui.next_btn.clicked.connect(self.turn_page_next)
self.ui.prev_btn.clicked.connect(self.turn_page_prev)
@@ -57,6 +53,7 @@ def __init__(self, parent=None):
self.__filter_controller.set_orders_container(self.ui.orders_grid)
self.__filter_controller.set_genres_container(self.Form_genres)
+ @override
def setup(self):
if not self.catalog:
self.ui.catalogs_frame.hide()
@@ -68,7 +65,8 @@ def setup(self):
else:
self.get_content()
- def setup_manga_item(self, manga: Manga):
+ @override
+ def _setup_manga_item(self, manga: Manga):
item = MangaItem(manga, pool=self.manga_area.manga_thread_pool)
item.manga_clicked.connect(self.manga_open.emit)
return item
@@ -79,6 +77,7 @@ def change_catalog(self, index: int):
self.setup_filters()
self.apply_filter()
+ @override
def update_page(self):
self.ui.page_label.setText(
f"{translate('Other', 'Page')} {self.request_params.page}",
diff --git a/nlightreader/widgets/NlightTemplates/History.py b/nlightreader/widgets/NlightTemplates/History.py
index 80d9d8f2..5dec26d2 100644
--- a/nlightreader/widgets/NlightTemplates/History.py
+++ b/nlightreader/widgets/NlightTemplates/History.py
@@ -3,10 +3,11 @@
from qfluentwidgets import FluentIcon
from data.ui.widgets.history import Ui_Form
-from nlightreader.consts.colors import ItemsColors
+from nlightreader.consts.colors import ItemsIcons
from nlightreader.contexts import HistoryNoteMenu
-from nlightreader.items import HistoryNote, Manga
-from nlightreader.utils import Database
+from nlightreader.items import HistoryNote
+from nlightreader.models import Manga
+from nlightreader.utils.database import Database
class FormHistory(QWidget):
@@ -39,7 +40,7 @@ def set_as_read():
True,
),
)
- selected_item.setBackground(0, ItemsColors.READ)
+ selected_item.setIcon(0, ItemsIcons.READ)
def remove_all():
self.db.del_history_notes(selected_manga)
@@ -91,9 +92,9 @@ def update_content(self):
for note in self.sorted_notes[manga]:
ch_item = QTreeWidgetItem([note.chapter.get_name()])
if note.is_completed:
- ch_item.setBackground(0, ItemsColors.READ)
+ ch_item.setIcon(0, ItemsIcons.READ.qicon())
else:
- ch_item.setBackground(0, ItemsColors.UNREAD)
+ ch_item.setIcon(0, ItemsIcons.UNREAD)
top_item.addChild(ch_item)
def _get_selected_note(self) -> HistoryNote:
diff --git a/nlightreader/widgets/NlightTemplates/Info.py b/nlightreader/widgets/NlightTemplates/Info.py
index b16fcc91..af181ff2 100644
--- a/nlightreader/widgets/NlightTemplates/Info.py
+++ b/nlightreader/widgets/NlightTemplates/Info.py
@@ -6,23 +6,22 @@
from qfluentwidgets import FluentIcon
from data.ui.widgets.info import Ui_Form
+from nlightreader.consts.colors import ItemsIcons
from nlightreader.consts.enums import LIB_LISTS, Nl
from nlightreader.consts.files import NlFluentIcons
from nlightreader.contexts import ReadMarkMenu
from nlightreader.dialogs import FormCharacter, FormRate
-from nlightreader.items import Chapter, Character, HistoryNote, Manga
+from nlightreader.items import HistoryNote
+from nlightreader.models import Chapter, Character, Manga
from nlightreader.parsers.catalog import AbstractCatalog
-from nlightreader.utils import (
- Database,
- description_to_html,
- FileManager,
- get_catalog,
- get_language_icon,
- get_status,
- translate,
- Worker,
-)
+from nlightreader.utils.catalog_manager import get_catalog_by_id
+from nlightreader.utils.database import Database
+from nlightreader.utils.file_manager import FileManager
from nlightreader.utils.html_video import start_html_video
+from nlightreader.utils.text_formatter import description_to_html
+from nlightreader.utils.threads import Worker
+from nlightreader.utils.translator import translate
+from nlightreader.utils.utils import get_language_icon
from nlightreader.widgets.NlightWidgets import ChapterTreeItem
from nlightreader.windows.Reader import ReaderWindow
@@ -70,26 +69,26 @@ def __init__(self):
self.ui.scrollArea.resizeEvent = self.scroll_area_resize_event
- self.db: Database = Database()
- self.thread_pool = QThreadPool()
- self.thread_pool.setMaxThreadCount(3)
- self.catalog: AbstractCatalog | None = None
- self.manga = None
- self.related_mangas: list[Manga] = []
- self.related_characters: list[Character] = []
- self.chapters: list[Chapter] = []
- self.sorted_chapters = {}
- self.manga_pixmap = None
- self.reader_window = None
- self.rate_window = None
- self.character_window = None
+ self.__db: Database = Database()
+ self.__thread_pool = QThreadPool()
+ self.__thread_pool.setMaxThreadCount(3)
+ self.__catalog: AbstractCatalog | None = None
+ self.__manga = None
+ self.__related_mangas: list[Manga] = []
+ self.__related_characters: list[Character] = []
+ self.__chapters: list[Chapter] = []
+ self.__sorted_chapters = {}
+ self.__manga_pixmap = None
+ self.__reader_window = None
+ self.__rate_window = None
+ self.__character_window = None
def on_context_menu(self, pos):
context_target = self.ui.items_tree
def set_as_read_all():
history_notes = []
- chapters_by_lang: list[Chapter] = self.sorted_chapters[
+ chapters_by_lang: list[Chapter] = self.__sorted_chapters[
selected_chapter.language
][selected_chapter.translator]
for i, chapter in enumerate(
@@ -101,18 +100,18 @@ def set_as_read_all():
],
):
history_notes.append(
- HistoryNote(chapter, self.manga, True),
+ HistoryNote(chapter, self.__manga, True),
)
item = selected_item.parent().child(i)
item.setIcon(
0,
FluentIcon.ACCEPT_MEDIUM.qicon(),
)
- self.db.add_history_notes(history_notes)
+ self.__db.add_history_notes(history_notes)
def set_as_read():
- self.db.add_history_note(
- HistoryNote(selected_chapter, self.manga, True),
+ self.__db.add_history_note(
+ HistoryNote(selected_chapter, self.__manga, True),
)
selected_item.setIcon(
0,
@@ -120,7 +119,7 @@ def set_as_read():
)
def remove_read_state():
- self.db.del_history_note(selected_chapter)
+ self.__db.del_history_note(selected_chapter)
selected_item.setIcon(0, QIcon())
menu = ReadMarkMenu()
@@ -128,10 +127,10 @@ def remove_read_state():
if not selected_item or not isinstance(selected_item, ChapterTreeItem):
return
selected_chapter = selected_item.chapter
- if not self.db.check_complete_chapter(selected_chapter):
+ if not self.__db.check_complete_chapter(selected_chapter):
menu.set_mode(0)
else:
- if self.db.get_complete_status(selected_chapter):
+ if self.__db.get_complete_status(selected_chapter):
menu.set_mode(1)
else:
menu.set_mode(2)
@@ -141,7 +140,7 @@ def remove_read_state():
menu.exec(context_target.mapToGlobal(pos))
def resizeEvent(self, event):
- if not self.catalog or not self.manga or not self.manga_pixmap:
+ if not self.__catalog or not self.__manga or not self.__manga_pixmap:
return
self.update_manga_preview()
@@ -151,32 +150,36 @@ def scroll_area_resize_event(self, event):
)
def sort_chapters(self):
- self.sorted_chapters.clear()
- for chapter in self.chapters:
+ self.__sorted_chapters.clear()
+ for chapter in self.__chapters:
ch_lang = chapter.language
- if ch_lang not in self.sorted_chapters:
- self.sorted_chapters[ch_lang] = {}
- if chapter.translator not in self.sorted_chapters[ch_lang]:
- self.sorted_chapters[ch_lang][chapter.translator] = []
- (self.sorted_chapters[ch_lang][chapter.translator].append(chapter))
+ if ch_lang not in self.__sorted_chapters:
+ self.__sorted_chapters[ch_lang] = {}
+ if chapter.translator not in self.__sorted_chapters[ch_lang]:
+ self.__sorted_chapters[ch_lang][chapter.translator] = []
+ (
+ self.__sorted_chapters[ch_lang][chapter.translator].append(
+ chapter,
+ )
+ )
def _get_selected_chapter(self) -> Chapter | None:
selected_item = self.ui.items_tree.currentItem()
if not isinstance(selected_item, ChapterTreeItem):
- return
+ return None
return selected_item.chapter
def get_selected_related_title(self):
- return self.catalog.get_manga(
- self.related_mangas[self.ui.related_list.currentIndex().row()],
+ return self.__catalog.get_manga(
+ self.__related_mangas[self.ui.related_list.currentIndex().row()],
)
def setup(self, manga):
def info_setup():
try:
- self.catalog = get_catalog(manga.catalog_id)()
- self.manga = self.catalog.get_manga(manga)
- self.db.add_manga(self.manga)
+ self.__catalog = get_catalog_by_id(manga.catalog_id)
+ self.__manga = self.__catalog.get_manga(manga)
+ self.__db.add_manga(self.__manga)
except Exception as e:
logging.error(e)
self.setup_error.emit()
@@ -184,7 +187,7 @@ def info_setup():
Worker(
target=info_setup,
callback=self.update_additional_info,
- ).start(pool=self.thread_pool)
+ ).start(pool=self.__thread_pool)
def update_add_button_icon(self):
if self.ui.add_btn.isChecked():
@@ -193,12 +196,12 @@ def update_add_button_icon(self):
self.ui.add_btn.setIcon(FluentIcon.ADD_TO)
def update_additional_info(self):
- self.ui.lib_frame.setVisible(not self.catalog.is_primary)
- self.ui.shikimori_frame.setVisible(self.catalog.is_primary)
+ self.ui.lib_frame.setVisible(not self.__catalog.is_primary)
+ self.ui.shikimori_frame.setVisible(self.__catalog.is_primary)
self.set_info()
- if self.db.check_manga_library(self.manga):
+ if self.__db.check_manga_library(self.__manga):
self.ui.lib_list_box.setCurrentIndex(
- self.db.get_manga_library_list(self.manga).value,
+ self.__db.get_manga_library_list(self.__manga).value,
)
self.ui.add_btn.setChecked(True)
else:
@@ -208,48 +211,48 @@ def update_additional_info(self):
Worker(
target=self.get_chapters,
callback=self.update_chapters,
- ).start(pool=self.thread_pool)
+ ).start(pool=self.__thread_pool)
Worker(
target=self.get_relations,
callback=self.update_relations,
- ).start(pool=self.thread_pool)
+ ).start(pool=self.__thread_pool)
Worker(
target=self.get_characters,
callback=self.update_characters,
- ).start(pool=self.thread_pool)
+ ).start(pool=self.__thread_pool)
self.setup_done.emit()
@Slot()
def open_rate(self):
- self.rate_window = FormRate(self.manga, parent=self)
- self.rate_window.exec()
+ self.__rate_window = FormRate(self.__manga, parent=self)
+ self.__rate_window.exec()
@Slot()
def open_character(self):
- character = self.catalog.get_character(
- self.related_characters[
+ character = self.__catalog.get_character(
+ self.__related_characters[
self.ui.characters_list.currentIndex().row()
],
)
- self.character_window = FormCharacter(
+ self.__character_window = FormCharacter(
character,
- self.manga.catalog_id,
+ self.__manga.catalog_id,
parent=self,
)
- self.character_window.show()
+ self.__character_window.show()
def update_manga_preview(self):
self.ui.image.clear()
- if not self.manga_pixmap:
- self.manga_pixmap = FileManager.get_manga_preview(
- self.manga,
- self.catalog,
+ if not self.__manga_pixmap:
+ self.__manga_pixmap = FileManager.get_manga_preview(
+ self.__manga,
+ self.__catalog,
)
image_size = QSize(self.width() // 5, self.height() // 2)
- pixmap = self.manga_pixmap.scaled(
+ pixmap = self.__manga_pixmap.scaled(
image_size,
Qt.AspectRatioMode.KeepAspectRatio,
Qt.TransformationMode.SmoothTransformation,
@@ -258,54 +261,55 @@ def update_manga_preview(self):
self.ui.image.setPixmap(pixmap)
def set_info(self):
- self.ui.name_label.setText(self.manga.name)
- self.ui.russian_label.setText(self.manga.russian)
- self.ui.status_label.setVisible(bool(self.manga.status))
+ self.ui.name_label.setText(self.__manga.name)
+ self.ui.russian_label.setText(self.__manga.russian)
+ self.ui.status_label.setVisible(bool(self.__manga.status))
self.ui.status_label.setText(
- f"{translate('Other', 'Status')}: {get_status(self.manga.status)}",
+ f"{translate('Other', 'Status')}: "
+ f"{translate('Status', self.__manga.status.to_str())}",
)
- self.ui.volumes_label.setVisible(bool(self.manga.volumes))
- self.ui.chapters_label.setVisible(bool(self.manga.chapters))
+ self.ui.volumes_label.setVisible(bool(self.__manga.volumes))
+ self.ui.chapters_label.setVisible(bool(self.__manga.chapters))
self.ui.volumes_label.setText(
- f"{translate('Other', 'Volumes')}: {self.manga.volumes}",
+ f"{translate('Other', 'Volumes')}: {self.__manga.volumes}",
)
self.ui.chapters_label.setText(
- f"{translate('Other', 'Chapters')}: {self.manga.chapters}",
+ f"{translate('Other', 'Chapters')}: {self.__manga.chapters}",
)
- self.ui.catalog_score_label.setVisible(bool(self.manga.score))
+ self.ui.catalog_score_label.setVisible(bool(self.__manga.score))
self.ui.catalog_score_label.setText(
- f"{translate('Other', 'Rating')}: {self.manga.score}",
+ f"{translate('Other', 'Rating')}: {self.__manga.score}",
)
- # self.ui.description_frame.setVisible(bool(self.manga.get_description()))
+ # self.ui.description_frame.setVisible(bool(self.__manga.get_description()))
self.ui.description_text.setHtml(
- description_to_html(self.manga.get_description()),
+ description_to_html(self.__manga.get_description()),
)
@Slot()
def add_to_favorites(self):
- if self.db.check_manga_library(self.manga):
- self.db.rem_manga_library(self.manga)
+ if self.__db.check_manga_library(self.__manga):
+ self.__db.rem_manga_library(self.__manga)
else:
lib_list = Nl.LibList(self.ui.lib_list_box.currentIndex())
- self.db.add_manga_library(self.manga, lib_list)
+ self.__db.add_manga_library(self.__manga, lib_list)
self.update_add_button_icon()
@Slot()
def change_lib_list(self):
- if self.db.check_manga_library(self.manga):
+ if self.__db.check_manga_library(self.__manga):
lib_list = Nl.LibList(self.ui.lib_list_box.currentIndex())
- self.db.add_manga_library(self.manga, lib_list)
+ self.__db.add_manga_library(self.__manga, lib_list)
def get_chapters(self):
- self.chapters = self.catalog.get_chapters(self.manga)
- self.chapters.reverse()
+ self.__chapters = self.__catalog.get_chapters(self.__manga)
+ self.__chapters.reverse()
self.sort_chapters()
- self.db.add_chapters(self.chapters, self.manga)
+ self.__db.add_chapters(self.__chapters, self.__manga)
def update_chapters(self):
self.ui.items_tree.clear()
- self.ui.items_frame.setVisible(bool(self.chapters))
- for lang, translators in self.sorted_chapters.items():
+ self.ui.items_frame.setVisible(bool(self.__chapters))
+ for lang, translators in self.__sorted_chapters.items():
lang_item = QTreeWidgetItem(
[translate("NlLanguage", lang.to_str())],
)
@@ -320,55 +324,54 @@ def update_chapters(self):
for chapter in chapters:
ch_item = ChapterTreeItem(chapter)
- if self.db.check_complete_chapter(chapter):
- if self.db.get_complete_status(chapter):
- ch_item.setIcon(
- 0,
- FluentIcon.ACCEPT_MEDIUM.qicon(),
- )
+ if self.__db.check_complete_chapter(chapter):
+ if self.__db.get_complete_status(chapter):
+ ch_item.setIcon(0, ItemsIcons.READ.qicon())
+ else:
+ ch_item.setIcon(0, ItemsIcons.UNREAD)
translator_item.addChild(ch_item)
- if len(self.sorted_chapters) == 1:
+ if len(self.__sorted_chapters) == 1:
lang_item.setExpanded(True)
def get_relations(self):
- self.related_mangas = self.catalog.get_relations(self.manga)
+ self.__related_mangas = self.__catalog.get_relations(self.__manga)
def update_relations(self):
self.ui.related_list.clear()
- self.ui.related_frame.setVisible(bool(self.related_mangas))
- for manga in self.related_mangas:
+ self.ui.related_frame.setVisible(bool(self.__related_mangas))
+ for manga in self.__related_mangas:
item = QListWidgetItem(manga.get_name())
self.ui.related_list.addItem(item)
def get_characters(self):
- self.related_characters = self.catalog.get_characters(self.manga)
+ self.__related_characters = self.__catalog.get_characters(self.__manga)
def update_characters(self):
self.ui.characters_list.clear()
- self.ui.characters_frame.setVisible(bool(self.related_characters))
- for character in self.related_characters:
+ self.ui.characters_frame.setVisible(bool(self.__related_characters))
+ for character in self.__related_characters:
item = QListWidgetItem(character.get_name())
self.ui.characters_list.addItem(item)
@Slot()
def open_reader(self):
try:
- if self.reader_window is not None:
- self.reader_window.close()
+ if self.__reader_window is not None:
+ self.__reader_window.close()
except RuntimeError:
pass
finally:
selected_chapter = self._get_selected_chapter()
if selected_chapter:
if hasattr(selected_chapter, "url"):
- start_html_video(self.manga, selected_chapter)
+ start_html_video(self.__manga, selected_chapter)
return
- self.reader_window = ReaderWindow()
- self.reader_window.setup(
- self.manga,
- self.chapters,
- self.chapters.index(selected_chapter) + 1,
+ self.__reader_window = ReaderWindow()
+ self.__reader_window.setup(
+ self.__manga,
+ self.__chapters,
+ self.__chapters.index(selected_chapter) + 1,
)
@Slot()
diff --git a/nlightreader/widgets/NlightTemplates/Library.py b/nlightreader/widgets/NlightTemplates/Library.py
index 1befbe4c..66c2ed7b 100644
--- a/nlightreader/widgets/NlightTemplates/Library.py
+++ b/nlightreader/widgets/NlightTemplates/Library.py
@@ -1,7 +1,9 @@
+from typing import override
+
from data.ui.widgets.library import Ui_Form
from nlightreader.consts.enums import Nl
-from nlightreader.items import Manga
+from nlightreader.models import Manga
from nlightreader.parsers import LocalLib
from nlightreader.widgets.NlightTemplates.BaseWidget import (
MangaItemBasedWidget,
@@ -18,9 +20,6 @@ def __init__(self, parent=None):
self.setObjectName("FormLibrary")
self.manga_area.install(self.ui.items_layout)
- self.manga_area.get_content_widget().layout().addWidget(
- self.progressRing,
- )
self.ui.planned_btn.clicked.connect(
lambda: self.change_list(Nl.LibList.planned),
@@ -42,15 +41,9 @@ def __init__(self, parent=None):
)
self.catalog = LocalLib()
- def update_content(self):
- self.mangas = self.catalog.search_manga(self.request_params)
- super().update_content()
-
- def setup_manga_item(self, manga: Manga):
+ @override
+ def _setup_manga_item(self, manga: Manga):
item = MangaItem(manga, pool=self.manga_area.manga_thread_pool)
item.manga_clicked.connect(self.manga_open.emit)
item.manga_changed.connect(self.get_content)
return item
-
- def get_content(self):
- self.update_content()
diff --git a/nlightreader/widgets/NlightTemplates/Shikimori.py b/nlightreader/widgets/NlightTemplates/Shikimori.py
index fccfc324..9df93f41 100644
--- a/nlightreader/widgets/NlightTemplates/Shikimori.py
+++ b/nlightreader/widgets/NlightTemplates/Shikimori.py
@@ -1,12 +1,16 @@
+from typing import override
+
from PySide6.QtCore import Slot
from qfluentwidgets import FluentIcon
from data.ui.widgets.shikimori import Ui_Form
from nlightreader.consts.enums import Nl
from nlightreader.dialogs import TokenAuthMessageBox, UserDataAuthMessageBox
-from nlightreader.items import Manga, User
+from nlightreader.items import User
+from nlightreader.models import Manga
from nlightreader.parsers import ShikimoriLib
-from nlightreader.utils import translate, Worker
+from nlightreader.utils.threads import Worker
+from nlightreader.utils.translator import translate
from nlightreader.widgets.NlightTemplates.BaseWidget import (
MangaItemBasedWidget,
)
@@ -25,9 +29,6 @@ def __init__(self, parent=None):
self.setObjectName("FormShikimori")
self.manga_area.install(self.ui.items_layout)
- self.manga_area.get_content_widget().layout().addWidget(
- self.progressRing,
- )
self.ui.planned_btn.clicked.connect(
lambda: self.change_list(Nl.LibList.planned),
@@ -54,7 +55,8 @@ def __init__(self, parent=None):
self.catalog = ShikimoriLib()
Worker(target=self.get_user_info, callback=self.set_user_info).start()
- def setup_manga_item(self, manga: Manga):
+ @override
+ def _setup_manga_item(self, manga: Manga):
item = MangaItem(
manga,
is_added_to_lib=False,
@@ -70,18 +72,22 @@ def get_user_info(self):
def set_user_info(self, user: User):
if user.nickname:
self.ui.auth_btn.setText(user.nickname)
- self.get_content()
else:
self.ui.auth_btn.setText(
translate("Other", "Sign in"),
)
self.ui.auth_btn.setEnabled(True)
+ @override
def update_page(self):
self.ui.page_label.setText(
f"{translate('Other', 'Page')} {self.request_params.page}",
)
+ def auth_success_callback(self, user: User):
+ self.set_user_info(user)
+ self.get_content()
+
@Slot()
def authorize(self):
if self.catalog.fields == 1:
@@ -92,7 +98,7 @@ def authorize(self):
self.catalog.session.auth_login(w.get_user_data())
Worker(
target=self.get_user_info,
- callback=self.set_user_info,
+ callback=self.auth_success_callback,
).start()
@Slot()
diff --git a/nlightreader/widgets/NlightWidgets/manga_item.py b/nlightreader/widgets/NlightWidgets/manga_item.py
index a3929744..d056f3d9 100644
--- a/nlightreader/widgets/NlightWidgets/manga_item.py
+++ b/nlightreader/widgets/NlightWidgets/manga_item.py
@@ -8,14 +8,12 @@
from data.ui.manga_item import Ui_Form
from nlightreader.contexts import LibraryMangaMenu
-from nlightreader.items import Manga
-from nlightreader.utils import (
- Database,
- FileManager,
- get_catalog,
- translate,
- Worker,
-)
+from nlightreader.models import Manga
+from nlightreader.utils.catalog_manager import get_catalog_by_id
+from nlightreader.utils.database import Database
+from nlightreader.utils.file_manager import FileManager
+from nlightreader.utils.threads import Worker
+from nlightreader.utils.translator import translate
class MangaItem(QWidget):
@@ -26,14 +24,14 @@ def __init__(self, manga: Manga, *, is_added_to_lib=True, pool=None):
super().__init__()
self.ui = Ui_Form()
self.ui.setupUi(self)
- self.manga = manga
- self._catalog = get_catalog(self.manga.catalog_id)()
- self.manga_pixmap = None
- self._is_added_to_lib = is_added_to_lib
- self._db: Database = Database()
- self._pool = pool
+ self.__manga = manga
+ self.__catalog = get_catalog_by_id(self.__manga.catalog_id)
+ self.__manga_pixmap = None
+ self.__is_added_to_lib = is_added_to_lib
+ self.__db: Database = Database()
+ self.__pool = pool
self.customContextMenuRequested.connect(self.on_context_menu)
- self.ui.name_lbl.setText(self.manga.get_name())
+ self.ui.name_lbl.setText(self.__manga.get_name())
def enterEvent(self, event):
super().enterEvent(event)
@@ -48,61 +46,60 @@ def mouseReleaseEvent(self, event):
super().mouseReleaseEvent(event)
if event.button() == Qt.MouseButton.LeftButton:
if self.rect().contains(event.pos()):
- self.manga_clicked.emit(self.manga)
+ self.manga_clicked.emit(self.__manga)
def on_context_menu(self, pos):
- catalog = get_catalog(self.manga.catalog_id)()
- manga_title = self.manga.get_name()
+ manga_title = self.__manga.get_name()
info_bar_parent = self.parentWidget().parentWidget()
info_bar_duration = 2000
def add_to_lib():
- self._db.add_manga(self.manga)
- self._db.add_manga_library(self.manga)
+ self.__db.add_manga(self.__manga)
+ self.__db.add_manga_library(self.__manga)
InfoBar.success(
title=manga_title,
content=translate(
"Message",
"Manga {} has been added.",
- ).format(self.manga.get_name()),
+ ).format(self.__manga.get_name()),
duration=info_bar_duration,
parent=info_bar_parent,
)
def remove_from_lib():
- self._db.rem_manga_library(self.manga)
+ self.__db.rem_manga_library(self.__manga)
InfoBar.success(
title=manga_title,
content=translate(
"Message",
"Manga {} has been deleted.",
- ).format(self.manga.get_name()),
+ ).format(self.__manga.get_name()),
duration=info_bar_duration,
parent=info_bar_parent,
)
self.manga_changed.emit()
def open_in_browser():
- webbrowser.open_new_tab(catalog.get_manga_url(self.manga))
+ webbrowser.open_new_tab(self.__catalog.get_manga_url(self.__manga))
def remove_files():
- FileManager.remove_manga_files(self.manga, catalog)
+ FileManager.remove_manga_files(self.__manga, self.__catalog)
InfoBar.success(
title=manga_title,
content=translate(
"Message",
"Files {} have been removed.",
- ).format(self.manga.get_name()),
+ ).format(self.__manga.get_name()),
duration=info_bar_duration,
parent=info_bar_parent,
)
def open_local_files():
- FileManager.open_dir_in_explorer(self.manga, catalog)
+ FileManager.open_dir_in_explorer(self.__manga, self.__catalog)
menu = LibraryMangaMenu()
- if self._is_added_to_lib and not self._catalog.is_primary:
- if self._db.check_manga_library(self.manga):
+ if self.__is_added_to_lib and not self.__catalog.is_primary:
+ if self.__db.check_manga_library(self.__manga):
menu.set_mode(1)
else:
menu.set_mode(0)
@@ -122,17 +119,17 @@ def set_size(self, size: int):
self.setFixedWidth(max_size.width())
self.ui.image_card.setFixedSize(max_size)
self.ui.image.setMaximumSize(max_size)
- if self.manga_pixmap:
+ if self.__manga_pixmap:
self.set_image()
def get_image(self):
- self.manga_pixmap = FileManager.get_manga_preview(
- self.manga,
- self._catalog,
+ self.__manga_pixmap = FileManager.get_manga_preview(
+ self.__manga,
+ self.__catalog,
)
def set_image(self, opacity: float = 1.0):
- if not self.manga_pixmap:
+ if not self.__manga_pixmap:
return
image = QImage(
@@ -168,7 +165,7 @@ def set_image(self, opacity: float = 1.0):
painter.drawPixmap(
0,
0,
- self.manga_pixmap.scaled(
+ self.__manga_pixmap.scaled(
self.ui.image.maximumSize(),
),
)
@@ -180,4 +177,4 @@ def update_image(self):
Worker(
target=self.get_image,
callback=self.set_image,
- ).start(self._pool)
+ ).start(self.__pool)
diff --git a/nlightreader/widgets/NlightWidgets/title_tree_item.py b/nlightreader/widgets/NlightWidgets/title_tree_item.py
index 27e4f196..61f8c727 100644
--- a/nlightreader/widgets/NlightWidgets/title_tree_item.py
+++ b/nlightreader/widgets/NlightWidgets/title_tree_item.py
@@ -1,6 +1,6 @@
from PySide6.QtWidgets import QTreeWidgetItem
-from nlightreader.items import Chapter
+from nlightreader.models import Chapter
class ChapterTreeItem(QTreeWidgetItem):
diff --git a/nlightreader/widgets/interfaces/__init__.py b/nlightreader/widgets/interfaces/__init__.py
new file mode 100644
index 00000000..3debaf3c
--- /dev/null
+++ b/nlightreader/widgets/interfaces/__init__.py
@@ -0,0 +1 @@
+from .settings_intefrace import SettingsInterface
diff --git a/nlightreader/widgets/interfaces/settings_intefrace.py b/nlightreader/widgets/interfaces/settings_intefrace.py
new file mode 100644
index 00000000..3f9b4adb
--- /dev/null
+++ b/nlightreader/widgets/interfaces/settings_intefrace.py
@@ -0,0 +1,186 @@
+from qfluentwidgets import (
+ BodyLabel,
+ ExpandLayout,
+ HyperlinkCard,
+ InfoBar,
+ OptionsSettingCard,
+ PrimaryPushSettingCard,
+ ScrollArea,
+ SettingCardGroup,
+ SwitchSettingCard,
+)
+from qfluentwidgets import FluentIcon
+from PySide6.QtCore import Qt, Signal
+from PySide6.QtWidgets import QWidget
+from nlightreader.consts.app import APP_VERSION
+from nlightreader.utils.config import cfg
+
+
+class SettingsInterface(ScrollArea):
+ check_for_updates_signal = Signal()
+ theme_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setObjectName("SettingsInterface")
+ self.scrollWidget = QWidget()
+ self.expandLayout = ExpandLayout(self.scrollWidget)
+ self.setStyleSheet(
+ """
+ QWidget {background: transparent;}
+ QScrollArea {border: none;}
+ BodyLabel {font: 33px 'Microsoft YaHei Light';}
+ """,
+ )
+ self.settingLabel = BodyLabel(
+ self.tr("Settings"),
+ self,
+ )
+
+ self.personalGroup = SettingCardGroup(
+ self.tr("Personalization"),
+ self.scrollWidget,
+ )
+ self.themeCard = OptionsSettingCard(
+ cfg.theme_mode,
+ FluentIcon.BRUSH,
+ self.tr("Application theme"),
+ self.tr("Change the appearance of application"),
+ texts=[
+ self.tr("Light"),
+ self.tr("Dark"),
+ self.tr("Use system setting"),
+ ],
+ parent=self.personalGroup,
+ )
+ self.zoomCard = OptionsSettingCard(
+ cfg.dpi_scale,
+ FluentIcon.ZOOM,
+ self.tr("Interface zoom"),
+ self.tr("Change the size of widgets and fonts"),
+ texts=[
+ "100%",
+ "125%",
+ "150%",
+ "175%",
+ "200%",
+ self.tr("Use system setting"),
+ ],
+ parent=self.personalGroup,
+ )
+ self.languageCard = OptionsSettingCard(
+ cfg.language,
+ FluentIcon.LANGUAGE,
+ self.tr("Language"),
+ self.tr("Set your preferred language for UI"),
+ texts=[
+ "Русский",
+ "Українська",
+ "English",
+ self.tr("Use system setting"),
+ ],
+ parent=self.personalGroup,
+ )
+
+ self.utilsSoftwareGroup = SettingCardGroup(
+ self.tr("Episodes"),
+ self.scrollWidget,
+ )
+ self.enableKodikServerCard = SwitchSettingCard(
+ FluentIcon.ACCEPT,
+ self.tr(
+ "Automatically mark episodes as watched",
+ ),
+ configItem=cfg.enable_kodik_server,
+ parent=self.utilsSoftwareGroup,
+ )
+
+ self.updateSoftwareGroup = SettingCardGroup(
+ self.tr("Software update"),
+ self.scrollWidget,
+ )
+ self.updateOnStartUpCard = SwitchSettingCard(
+ FluentIcon.UPDATE,
+ self.tr(
+ "Check for updates when the application starts",
+ ),
+ self.tr(
+ "The new version will be more stable and have more features",
+ ),
+ configItem=cfg.check_updates_at_startup,
+ parent=self.updateSoftwareGroup,
+ )
+
+ self.aboutGroup = SettingCardGroup(
+ self.tr("About"),
+ self.scrollWidget,
+ )
+ self.projectCard = HyperlinkCard(
+ "https://github.com/brandonzorn/Nlight/",
+ self.tr("Project on GitHub"),
+ FluentIcon.GITHUB,
+ self.tr("Project on GitHub"),
+ self.tr(
+ "",
+ ),
+ self.aboutGroup,
+ )
+ self.aboutCard = PrimaryPushSettingCard(
+ self.tr("Check for updates"),
+ FluentIcon.INFO,
+ self.tr("About"),
+ f"© 2022 brandonzorn. {self.tr('Version')} {APP_VERSION}",
+ self.aboutGroup,
+ )
+
+ self.__init_widget()
+
+ def setup(self):
+ pass
+
+ def __init_widget(self):
+ self.resize(1000, 800)
+ self.setHorizontalScrollBarPolicy(
+ Qt.ScrollBarPolicy.ScrollBarAlwaysOff,
+ )
+ self.setViewportMargins(0, 120, 0, 20)
+ self.setWidget(self.scrollWidget)
+ self.setWidgetResizable(True)
+
+ self.__init_layout()
+ self.__connect_signals()
+
+ def __init_layout(self):
+ self.settingLabel.move(60, 63)
+
+ self.personalGroup.addSettingCard(self.themeCard)
+ self.personalGroup.addSettingCard(self.zoomCard)
+ self.personalGroup.addSettingCard(self.languageCard)
+
+ self.utilsSoftwareGroup.addSettingCard(self.enableKodikServerCard)
+
+ self.updateSoftwareGroup.addSettingCard(self.updateOnStartUpCard)
+
+ self.aboutGroup.addSettingCard(self.projectCard)
+ self.aboutGroup.addSettingCard(self.aboutCard)
+
+ self.expandLayout.setSpacing(28)
+ self.expandLayout.setContentsMargins(60, 10, 60, 0)
+ self.expandLayout.addWidget(self.personalGroup)
+ self.expandLayout.addWidget(self.utilsSoftwareGroup)
+ self.expandLayout.addWidget(self.updateSoftwareGroup)
+ self.expandLayout.addWidget(self.aboutGroup)
+
+ def __show_restart_tooltip(self):
+ InfoBar.warning(
+ "",
+ self.tr(
+ "Changes will take effect after restarting the application",
+ ),
+ parent=self.window(),
+ )
+
+ def __connect_signals(self):
+ cfg.appRestartSig.connect(self.__show_restart_tooltip)
+ self.aboutCard.clicked.connect(self.check_for_updates_signal)
+ self.themeCard.optionChanged.connect(self.theme_changed)
diff --git a/nlightreader/windows/Parent.py b/nlightreader/windows/Parent.py
index 95e26b44..2fc891a9 100644
--- a/nlightreader/windows/Parent.py
+++ b/nlightreader/windows/Parent.py
@@ -1,9 +1,9 @@
from PySide6.QtCore import QSize, Slot
-from qfluentwidgets import FluentIcon, FluentWindow
+from qfluentwidgets import FluentIcon, FluentWindow, NavigationItemPosition
from nlightreader.consts.files.files import NlFluentIcons
-from nlightreader.items import Manga
-from nlightreader.utils import translate
+from nlightreader.models import Manga
+from nlightreader.utils.translator import translate
from nlightreader.widgets.NlightTemplates import (
FormFacial,
FormHistory,
@@ -11,6 +11,7 @@
FormLibrary,
FormShikimori,
)
+from nlightreader.widgets.interfaces import SettingsInterface
class ParentWindow(FluentWindow):
@@ -21,6 +22,9 @@ def __init__(self):
self.facial_interface = FormFacial()
self.shikimori_interface = FormShikimori()
self.history_interface = FormHistory()
+
+ self.settings_interface = SettingsInterface()
+
self.info_interface: FormInfo | None = None
self.library_interface.manga_open.connect(self.open_info)
@@ -53,6 +57,12 @@ def init_navigation(self):
FluentIcon.HISTORY,
translate("MainWindow", "History"),
)
+ self.addSubInterface(
+ self.settings_interface,
+ FluentIcon.SETTING,
+ translate("SettingsInterface", "Settings"),
+ position=NavigationItemPosition.BOTTOM,
+ )
@Slot(int)
def on_widget_change(self, value):
@@ -63,7 +73,7 @@ def on_widget_change(self, value):
):
self.delete_info_interface()
self.navigationInterface.setReturnButtonVisible(
- self.stackedWidget.count() > 4,
+ self.stackedWidget.count() > 5,
)
if self.stackedWidget.currentWidget().objectName() in (
"FormInfo",
diff --git a/nlightreader/windows/Reader.py b/nlightreader/windows/Reader.py
index 9ff62f73..dae88fcc 100644
--- a/nlightreader/windows/Reader.py
+++ b/nlightreader/windows/Reader.py
@@ -1,25 +1,27 @@
import time
from PySide6.QtCore import Qt, Slot
-from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QListWidgetItem, QMainWindow
-from qfluentwidgets import FluentIcon, IndeterminateProgressRing
+from qfluentwidgets import FluentIcon
from data.ui.windows.reader import Ui_ReaderWindow
-from nlightreader.consts.colors import ItemsColors
+from nlightreader.consts.colors import ItemsIcons
from nlightreader.consts.enums import Nl
-from nlightreader.items import Chapter, HistoryNote, Image, Manga
-from nlightreader.utils import (
- Database,
- FileManager,
- get_catalog,
- get_language_icon,
- Thread,
- translate,
+from nlightreader.exceptions.parser_content_exc import (
+ FetchContentError,
+ NoContentError,
)
+from nlightreader.items import HistoryNote
+from nlightreader.models import Chapter, Image, Manga
+from nlightreader.utils.catalog_manager import get_catalog_by_id
+from nlightreader.utils.database import Database
+from nlightreader.utils.file_manager import FileManager
+from nlightreader.utils.threads import Thread
+from nlightreader.utils.translator import translate
from nlightreader.widgets.NlightContainers import TextArea
from nlightreader.widgets.NlightContainers.content_container import (
AbstractContentContainer,
+ ContentContainerState,
)
from nlightreader.widgets.NlightContainers.image_area import ImageArea
@@ -54,45 +56,35 @@ def __init__(self):
self._set_image_thread = Thread(
target=self.get_content,
callback=self.update_image,
+ error_callback=self.__process_errors,
)
- self.content_container: AbstractContentContainer | None = None
+ self.__content_container: AbstractContentContainer | None = None
- self.progressRing = IndeterminateProgressRing()
- self.progressRing.setVisible(False)
-
- self.db: Database = Database()
- self.manga = None
- self.chapters: list[Chapter] = []
- self.images: list[Image] = []
- self.cur_chapter = 1
- self.max_chapters = 1
- self.cur_page = 1
- self.max_page = 1
- self.catalog = None
+ self.__db: Database = Database()
+ self.__manga = None
+ self.__chapters: list[Chapter] = []
+ self.__images: list[Image] = []
+ self.__cur_chapter = 1
+ self.__max_chapters = 1
+ self.__cur_page = 1
+ self.__max_page = 1
+ self.__catalog = None
def setup(self, manga: Manga, chapters: list[Chapter], cur_chapter=1):
self.ui.chapters_frame.hide()
- self.manga = manga
- self.setWindowTitle(self.manga.name)
- self.content_container = (
+ self.__manga = manga
+ self.setWindowTitle(self.__manga.name)
+ self.__content_container = (
TextArea()
- if (self.manga.kind == Nl.MangaKind.ranobe)
+ if (self.__manga.kind == Nl.MangaKind.ranobe)
else ImageArea()
)
- self.content_container.install(self.ui.reader_layout)
- (
- self.content_container.get_content_widget()
- .parent()
- .layout()
- .addWidget(
- self.progressRing,
- )
- )
- self.chapters = chapters
- self.cur_chapter = cur_chapter
- self.max_chapters = len(chapters)
- self.catalog = get_catalog(manga.catalog_id)()
+ self.__content_container.install(self.ui.reader_layout)
+ self.__chapters = chapters
+ self.__cur_chapter = cur_chapter
+ self.__max_chapters = len(chapters)
+ self.__catalog = get_catalog_by_id(manga.catalog_id)
self.showMaximized()
self.update_chapters_list()
self.update_chapter()
@@ -120,90 +112,89 @@ def change_chapters_list_visible(self):
@Slot()
def change_chapter(self):
- self.cur_chapter = self.ui.items_list.currentIndex().row() + 1
+ self.__cur_chapter = self.ui.items_list.currentIndex().row() + 1
self.update_chapter()
def update_chapters_list(self):
self.ui.items_list.clear()
- for chapter in self.chapters:
- item = QListWidgetItem(chapter.get_name())
- if self.db.check_complete_chapter(chapter):
- if self.db.get_complete_status(chapter):
- item.setBackground(ItemsColors.READ)
+ for chapter in self.__chapters:
+ ch_item = QListWidgetItem(chapter.get_name())
+ if self.__db.check_complete_chapter(chapter):
+ if self.__db.get_complete_status(chapter):
+ ch_item.setIcon(ItemsIcons.READ.qicon())
else:
- item.setBackground(ItemsColors.UNREAD)
- if chapter.language:
- item.setIcon(QIcon(get_language_icon(chapter.language)))
- self.ui.items_list.addItem(item)
+ ch_item.setIcon(ItemsIcons.UNREAD)
+ self.ui.items_list.addItem(ch_item)
@Slot()
def turn_page_next(self):
- self.db.add_history_note(
+ self.__db.add_history_note(
HistoryNote(
self._current_chapter,
- self.manga,
+ self.__manga,
False,
),
)
- if self.cur_page == self.max_page:
- self.db.add_history_note(
+ if self.__cur_page == self.__max_page:
+ self.__db.add_history_note(
HistoryNote(
self._current_chapter,
- self.manga,
+ self.__manga,
True,
),
)
self.turn_chapter_next()
else:
- self.cur_page += 1
+ self.__cur_page += 1
self.update_page()
@Slot()
def turn_page_prev(self):
- self.db.add_history_note(
+ self.__db.add_history_note(
HistoryNote(
self._current_chapter,
- self.manga,
+ self.__manga,
False,
),
)
- if self.cur_page == 1:
- self.db.del_history_note(self._current_chapter)
+ if self.__cur_page == 1:
+ self.__db.del_history_note(self._current_chapter)
self.turn_chapter_prev()
else:
- self.cur_page -= 1
+ self.__cur_page -= 1
self.update_page()
def update_page(self):
self.ui.page_label.setText(
- f"{translate('Other', 'Page')} {self.cur_page} / {self.max_page}",
+ f"{translate('Other', 'Page')} "
+ f"{self.__cur_page} / {self.__max_page}",
)
self.attach_image()
@Slot()
def turn_chapter_next(self):
- self.db.add_history_note(
+ self.__db.add_history_note(
HistoryNote(
self._current_chapter,
- self.manga,
+ self.__manga,
True,
),
)
- if self.cur_chapter == self.max_chapters:
+ if self.__cur_chapter == self.__max_chapters:
self.deleteLater()
else:
- self.cur_chapter += 1
+ self.__cur_chapter += 1
self.update_chapter()
@Slot()
def turn_chapter_prev(self):
- if self.cur_chapter == 1:
+ if self.__cur_chapter == 1:
return
- self.cur_chapter -= 1
+ self.__cur_chapter -= 1
self.update_chapter()
def update_chapter(self):
- self.cur_page = 1
+ self.__cur_page = 1
self.get_images()
self.update_page()
self.ui.chapter_label.setText(self._current_chapter.get_name())
@@ -211,57 +202,65 @@ def update_chapter(self):
def attach_image(self):
self._set_image_thread.terminate()
self._set_image_thread.wait()
- if not self.images:
+ if not self.__images:
return
- self.progressRing.setVisible(True)
- self.progressRing.start()
- self.content_container.get_content_widget().setVisible(False)
+ self.__content_container.set_state(ContentContainerState.fetch_content)
self._set_image_thread.start()
+ def __process_errors(self, exception: Exception):
+ try:
+ raise exception
+ except FetchContentError:
+ self.__content_container.set_state(
+ ContentContainerState.fetch_error,
+ )
+ except NoContentError:
+ self.__content_container.set_state(
+ ContentContainerState.no_content,
+ )
+
def get_content(self):
- page = self.cur_page
- chapter = self.cur_chapter
+ page = self.__cur_page
+ chapter = self.__cur_chapter
if not FileManager.check_image_exists(
- self.manga,
- self.chapters[chapter - 1],
- self.images[page - 1],
- self.catalog,
+ self.__manga,
+ self.__chapters[chapter - 1],
+ self.__images[page - 1],
+ self.__catalog,
):
time.sleep(0.25)
- if page != self.cur_page or chapter != self.cur_chapter:
- return
+ if page != self.__cur_page or chapter != self.__cur_chapter:
+ return None
- if self.manga.kind == Nl.MangaKind.ranobe:
+ if self.__manga.kind == Nl.MangaKind.ranobe:
return FileManager.get_chapter_text_file(
- self.manga,
- self.chapters[chapter - 1],
- self.images[page - 1],
- self.catalog,
+ self.__manga,
+ self.__chapters[chapter - 1],
+ self.__images[page - 1],
+ self.__catalog,
)
return FileManager.get_image_file(
- self.manga,
- self.chapters[chapter - 1],
- self.images[page - 1],
- self.catalog,
+ self.__manga,
+ self.__chapters[chapter - 1],
+ self.__images[page - 1],
+ self.__catalog,
)
def update_image(self, content):
- self.progressRing.stop()
- self.progressRing.setVisible(False)
- self.content_container.get_content_widget().setVisible(True)
- self.content_container.set_content(content)
+ self.__content_container.set_state(ContentContainerState.show_content)
+ self.__content_container.set_content(content)
def get_images(self):
chapter = self._current_chapter
- self.images = self.catalog.get_images(self.manga, chapter)
- if not self.images:
- self.images = [Image.get_empty_instance()]
- self.max_page = self.get_chapter_pages()
+ self.__images = self.__catalog.get_images(self.__manga, chapter)
+ if not self.__images:
+ self.__images = [Image("", 1, None)]
+ self.__max_page = self.get_chapter_pages()
def get_chapter_pages(self) -> int:
- return self.images[-1].page
+ return self.__images[-1].page_number
@property
def _current_chapter(self) -> Chapter:
- return self.chapters[self.cur_chapter - 1]
+ return self.__chapters[self.__cur_chapter - 1]
diff --git a/pyproject.toml b/pyproject.toml
index 3a3b89f6..20a72aaf 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -10,9 +10,16 @@ exclude = """
[tool.flake8]
max-line-length = 79
-inline-quotes="double"
-import-order-style="google"
-ignore = ["R503", "R502", "F401", "N802", "I100", "I202", "W503"]
+inline-quotes = "double"
+import-order-style = "google"
+ignore = [
+ "R503",
+ "F401",
+ "N802",
+ "I100",
+ "I202",
+ "W503",
+]
exclude = [
".git",
"__pycache__",
@@ -24,5 +31,5 @@ exclude = [
"venv-fluent",
"data",
"__init__.py",
- "nlight_res_rc.py"
+ "nlight_res_rc.py",
]
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 558f45a7..2f5bc0ec 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,3 +8,4 @@ render-html>=1.0.1
requests>=2.28.1
requests-oauthlib>=1.3.1
SQLAlchemy>=2.0.25
+validators>=0.33.0
diff --git a/requirements/prod.txt b/requirements/prod.txt
index 558f45a7..2f5bc0ec 100644
--- a/requirements/prod.txt
+++ b/requirements/prod.txt
@@ -8,3 +8,4 @@ render-html>=1.0.1
requests>=2.28.1
requests-oauthlib>=1.3.1
SQLAlchemy>=2.0.25
+validators>=0.33.0