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 + Show spoilers + + + Cancel + Cancel + + + Delete + Delete + + + Update + Update + + + List + List + + + Chapters read + Chapters read + + + Rating + Rating + + + Get code + Get code + + + Authorization code + Authorization code + + + Login + Login + + + Password + Password + + + Sign in + Sign in + + + + Form + + MainWindow + Nlight + + + Kind + Kind + + + Order + Order + + + Apply + Apply + + + Reset + Reset + + + Filters + Filters + + + Genres list + Genres list + + + Catalogs + Catalogs + + + Sign in + Sign in + + + Search + Search + + + Related + Related + + + Characters + Characters + + + Planned + Planned + + + Completed + Completed + + + Reading + Reading + + + Re-reading + Re-reading + + + On hold + On hold + + + Dropped + Dropped + + + + MainWindow + + MainWindow + Nlight + + + Main + Main + + + Library + Library + + + History + History + + + Shikimori + Shikimori + + + + Menu + + Open in browser + Open in browser + + + Remove from library + Remove from library + + + Add to Library + Add to Library + + + Mark as read + Mark as read + + + Remove all + Remove all + + + Mark as read all previous + Mark as read all previous + + + Remove read mark + Remove read mark + + + Clear local files + Clear local files + + + Open local files + Open local files + + + + Other + + Page + Page + + + Sign in + Sign in + + + Status + Status + + + Volumes + Volumes + + + Chapters + Chapters + + + Rating + Rating + + + Page is loading + Page is loading + + + + Status + + Ongoing + Ongoing + + + Released + Released + + + Frozen + Frozen + + + + NlLanguage + + Russian + Russian + + + Ukrainian + Ukrainian + + + Japanese + Japanese + + + English + English + + + Undefined + Undefined + + + + NlKind + + Manga + Manga + + + OEL-manga + OEL-manga + + + Rumanga + Rumanga + + + Manhwa + Manhwa + + + Manhua + Manhua + + + Oneshot + Oneshot + + + Comic + Comic + + + Western comic + Western comic + + + Rucomic + Rucomic + + + Indonesian comic + Indonesian comic + + + Doujin + Doujin + + + Ranobe + Ranobe + + + Other + Other + + + Undefined + Undefined + + + + Message + + Manga {} has been added. + Manga {} has been added. + + + Manga {} has been deleted. + Manga {} has been deleted. + + + Files {} have been removed. + Files {} have been removed. + + + Check for updates. + Check for updates. + + + Error checking for updates. + Error checking for updates. + + + No updates available. You are using the latest version. + No updates available. You are using the latest version. + + + New version {result} is available! You are currently on version {APP_VERSION}. + New version {result} is available! You are currently on version {APP_VERSION}. + + + No connection + No connection + + + Nothing found + Nothing found + + + + SettingsInterface + + Use system setting + Use system setting + + + Interface zoom + Interface zoom + + + Change the size of widgets and fonts + Change the size of widgets and fonts + + + Language + Language + + + Set your preferred language for UI + Set your preferred language for UI + + + Software update + Software update + + + Check for updates when the application starts + Check for updates when the application starts + + + The new version will be more stable and have more features + The new version will be more stable and have more features + + + Changes will take effect after restarting the application + Changes will take effect after restarting the application + + + Check for updates + Check for updates + + + Settings + Settings + + + Application theme + Application theme + + + Change the appearance of application + Change the appearance of application + + + Dark + Dark + + + Light + Light + + + Personalization + Personalization + + + About + About + + + Project on GitHub + Project on GitHub + + + Episodes + Episodes + + + Automatically mark episodes as watched + 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 @@ Sign in Войти + + Search + Поиск + Related Связанное @@ -223,9 +227,13 @@ Выходит - Completed + Released Издано + + Frozen + Заморожено + NlLanguage @@ -331,9 +339,104 @@ 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}. Новая версия {result} доступна! Вы используете версию {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 + Проект на GitHub + + + Episodes + Эпизоды + + + Automatically mark episodes as watched + Автоматически отмечать эпизоды как просмотренные + - + \ 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 @@ Sign in Увійти + + Search + Пошук + Related Пов'язане @@ -223,9 +227,13 @@ Виходить - Completed + Released Видано + + Frozen + Заморожено + NlLanguage @@ -331,9 +339,104 @@ 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}. Нова версія {result} доступна! Ви використовуєте версію {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 + Проект на GitHub + + + Episodes + Епізоди + + + Automatically mark episodes as watched + Автоматично позначати епізоди як переглянуті + - + \ 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