Compare commits

..

27 Commits

Author SHA1 Message Date
Renovate Bot
cb07904c1b fix(deps): lock file maintenance python dependencies
Some checks failed
renovate/artifacts Artifact file update failure
Code check / Check code (pull_request) Successful in 1m18s
2025-08-24 17:39:06 +00:00
05e0d9d846 fix(renovate): disable poetry (bug in upstream)
All checks were successful
Code check / Check code (push) Successful in 1m25s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-24 22:35:30 +05:00
81433d3c56 chore(changelog): update
All checks were successful
Code check / Check code (push) Successful in 1m38s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-24 22:13:50 +05:00
0ff66e282b fix(input_manager): enable Escape key to close dialogs
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-24 22:10:39 +05:00
831b7739ba fix(input-manager): enable drive list navigation with arrow keys in FileExplorer
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-24 22:05:33 +05:00
50e1dfda57 chore(changelog): update
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-24 20:56:41 +05:00
fcf04e521d feat(file-explorer): add automatic scrolling for drives layout
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-24 20:54:50 +05:00
74d0700d7c chore(renovate): use . for source uv
All checks were successful
Code check / Check code (push) Successful in 1m18s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-24 17:40:36 +05:00
0435c77630 chore(changelog): update
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-24 17:39:52 +05:00
1cf93a60c8 feat: added favorites to file explorer
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-24 17:37:59 +05:00
31247d21c3 chore(changelog): update
Some checks failed
renovate / renovate (push) Failing after 1m1s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-23 21:35:23 +05:00
c6017a7dce fix(file explorer): don't skip /run/media
All checks were successful
Code check / Check code (push) Successful in 1m22s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-23 21:23:39 +05:00
c74d209dbd chore(ci): replace uv github action to manual install
All checks were successful
Code check / Check code (pull_request) Successful in 1m25s
Code check / Check code (push) Successful in 1m20s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-23 21:04:59 +05:00
5b257d3b62 fix(ci): disable cache from node
All checks were successful
Code check / Check code (push) Successful in 1m59s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-23 20:53:17 +05:00
4dcf1dbe6d fix(ci): I forget @
Some checks failed
Code check / Check code (push) Failing after 54s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-23 20:50:47 +05:00
8d6fe4aa65 chore(ci): install node 20 for uv
Some checks failed
Code check / Check code (push) Failing after 4s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-23 20:48:40 +05:00
022eb3f1e9 chore(changelog): update
Some checks failed
Check Translations / check-translations (push) Successful in 46s
Code check / Check code (push) Failing after 10s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-23 20:41:42 +05:00
11b847ed05 chore(localization): update
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-23 20:36:02 +05:00
1e4e0127a4 fix(i18n): add translation for File Explorer window title
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-23 20:33:19 +05:00
c045aa7a56 fix(input_manager): correct button mappings for increase/decrease size actions
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-23 20:31:22 +05:00
f18e7bae6b chore(changelog): update
Some checks failed
Code check / Check code (push) Failing after 15s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-23 11:28:02 +05:00
dcf8904037 feat(input_manager): enable cursor movement in QLineEdit with left/right arrows
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-23 11:25:26 +05:00
f9d24e385d fix(input_manager): prevent tab switching when using left/right arrows in QLineEdit
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-23 11:22:06 +05:00
09028931be feat: use Backspace for move to parent directory in FileExplorer
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-23 11:12:15 +05:00
0294c90c54 chore(changelog): update
All checks were successful
Code check / Check code (push) Successful in 2m43s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-19 17:54:40 +05:00
17dfef2d27 chore(tray): drop
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-19 17:52:09 +05:00
Renovate Bot
f0690f8811 fix(deps): lock file maintenance python dependencies
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-08-14 22:33:43 +05:00
24 changed files with 725 additions and 492 deletions

View File

@@ -22,10 +22,16 @@ jobs:
steps: steps:
- uses: https://gitea.com/actions/checkout@v4 - uses: https://gitea.com/actions/checkout@v4
- name: Install uv - name: Set up Node.js
uses: https://github.com/astral-sh/setup-uv@v6 uses: https://gitea.com/actions/setup-node@v4
with: with:
enable-cache: true node-version: 20
- name: Install uv manually
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
source $HOME/.local/bin/env
uv --version
- name: Sync dependencies into venv - name: Sync dependencies into venv
run: uv sync --all-extras --dev run: uv sync --all-extras --dev

View File

@@ -12,10 +12,16 @@ jobs:
steps: steps:
- uses: https://gitea.com/actions/checkout@v4 - uses: https://gitea.com/actions/checkout@v4
- name: Install uv - name: Set up Node.js
uses: https://github.com/astral-sh/setup-uv@v6 uses: https://gitea.com/actions/setup-node@v4
with: with:
enable-cache: true node-version: 20
- name: Install uv manually
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
. $HOME/.local/bin/env
uv --version
- name: Download external renovate config - name: Download external renovate config
run: | run: |

View File

@@ -6,23 +6,34 @@
## [Unreleased] ## [Unreleased]
### Added ### Added
- Больше типов анимаций при открытии карточки игры (за подробностями в документацию) - Больше типов анимаций при открытии карточки игры (подробности см. в документации).
- Анимация при выходе из карточки игры (за подробностями в документацию) - Анимация при закрытии карточки игры (подробности см. в документации).
- Добавлен обработчик нажатий стрелок на клавиатуре в поле ввода (позволяет перемещаться между символами с помощью стрелок).
- Система быстрого доступа (избранного) в диалоге выбора файлов.
- Автоматическая прокрутка для панели дисков в диалоге выбора файлов.
- Возможность выбора папок и / или дисков в диалоге выбора файлов через клавиатуру.
- Поднятия в родительскую директорию в диалоге выбора файлов на BackSpace.
### Changed ### Changed
- Уменьшена длительность анимации открытия карточки с 800 до 350мс - Уменьшена длительность анимации открытия карточки с 800 до 350 мс.
- Контекстное меню при открытие теперь сразу фокусируется на первом элементе - Контекстное меню при открытии теперь сразу фокусируется на первом элементе.
- Анимации теперь можно настраивать через темы (за подробностями в документацию) - Анимации теперь можно настраивать через темы (подробности см. в документации).
- Общие json (steam_apps и anticheat_games) теперь перекачиваются если сломаны - Общие JSON-файлы (`steam_apps` и `anticheat_games`) теперь перекачиваются, если они повреждены.
- Временно удалена светлая тема - Временно удалена светлая тема.
- Добавление и удаление игр из Steam теперь не требует перезагрузки Steam - Добавление и удаление игр из Steam больше не требует перезапуска клиента.
- Обновлены все зависимости (затрагивает только AppImage).
- Удалён отдельный трей, так как у PortProton есть собственный.
### Fixed ### Fixed
- legendary list теперь не вызывается если вход в EGS не был произведён - `legendary list` теперь не вызывается, если вход в EGS не был выполнен.
- Скриншоты тем теперь не теряют в качестве при масштабе отличном от 100% - Скриншоты тем больше не теряют качество при масштабе, отличном от 100%.
- Данные от HLTB теперь не отображаются в карточке если нет данных о времени прохождения - Данные от HLTB теперь не отображаются в карточке, если нет информации о времени прохождения.
- Диалог добавления игры теперь не добавляет игру если exe не существует - Диалог добавления игры больше не добавляет игру, если `exe` не существует.
- Вкладки больше не переключаются стрелками, если фокус в поле ввода.
- Исправлено переключение слайдера: RT (Xbox) / R2 (PS), LT (Xbox) / L2 (PS).
- Переведен заголовок окна диалога выбора файлов.
- Отображение устройств смонтированных в /run/media в диалоге выбора файлов.
- Закрытие диалога добавления / редактирования игры и диалога выбора файлов через escape.
### Contributors ### Contributors
- @Alex Smith - @Alex Smith
@@ -32,24 +43,24 @@
## [0.1.4] - 2025-07-21 ## [0.1.4] - 2025-07-21
### Added ### Added
- Переводы в переопределениях (за подробностями в документацию) - Переводы в переопределениях (подробности см. в документации).
- Обложки и описания для всех автоинсталлов - Обложки и описания для всех автоинсталлов.
- Возможность указать ссылку для скачивания обложки в диалоге добавления игры - Возможность указать ссылку для скачивания обложки в диалоге добавления игры.
- Интеграция с howlongtobeat.com - Интеграция с howlongtobeat.com.
### Changed ### Changed
- Оптимизированны обложки автоинсталлов - Оптимизированы обложки автоинсталлов.
- Папка custom_data исключена из сборки модуля для уменьшение его размера - Папка `custom_data` исключена из сборки модуля для уменьшения его размера.
- Бейдж PortProton теперь открывает PortProtonDB - Бейдж PortProton теперь открывает PortProtonDB.
- Отключено переключение полноэкранного режима через F11 или кнопку Select на геймпаде в gamescope сессии - Отключено переключение полноэкранного режима через F11 или кнопку Select на геймпаде в Gamescope-сессии.
- Удалён аргумент `--session` так как тестирование gamescope сессии завершено - Удалён аргумент `--session`, так как тестирование Gamescope-сессии завершено.
- В контекстном меню игр без exe файла теперь отображается только пункт "Удалить из PortProton" - В контекстном меню игр без exe-файла теперь отображается только пункт «Удалить из PortProton».
### Fixed ### Fixed
- Запрос к GitHub API при загрузке legendary теперь не игнорирует настройки прокси - Запрос к GitHub API при загрузке legendary теперь учитывает настройки прокси.
- Путь к portprotonqt-session-select в оверлее - Путь к `portprotonqt-session-select` в оверлее.
- Работа exiftool в AppImage - Работа `exiftool` в AppImage.
- Открытие контекстного меню у игр без exe - Открытие контекстного меню у игр без exe-файла.
### Contributors ### Contributors
- @Vector_null - @Vector_null
@@ -59,32 +70,32 @@
## [0.1.3] - 2025-07-05 ## [0.1.3] - 2025-07-05
### Added ### Added
- Аргумент `--session` для запуска приложения в gamescope (Исключительно в целях тестирования) - Аргумент `--session` для запуска приложения в Gamescope (исключительно в целях тестирования).
- Начальная поддержка EGS (Без EOS, скачивания игр и запуска игр из сторонних магазинов) - Начальная поддержка EGS (без EOS, скачивания и запуска игр из сторонних магазинов).
- Автодополнение bash для комманды portprotonqt - Автодополнение bash для команды `portprotonqt`.
- Поддержка геймпадов в диалоге выбора игры - Поддержка геймпадов в диалоге выбора игры.
- Быстрый запуск и остановка игры через контекстное меню - Быстрый запуск и остановка игры через контекстное меню.
- Иконки в контекстом меню - Иконки в контекстном меню.
- Обложки для части автоинсталлов - Обложки для части автоинсталлов.
### Changed ### Changed
- Удалены сборки для Fedora 40 - Удалены сборки для Fedora 40.
- Перенесены параметры анимации GameCard в `styles.py` с подробной документацией для поддержки кастомизации тем. - Параметры анимации GameCard перенесены в `styles.py` с подробной документацией для кастомизации тем.
- Статус выделения и наведения на карточки теперь взаимоисключают друг друга - Статусы выделения и наведения на карточки теперь взаимоисключающие.
- Все desktop файлы создаются с коментарием "Запустить игру {название} через PortProton" - Все desktop-файлы создаются с комментарием «Запустить игру {название} через PortProton».
- Заполнители в переводах теперь стали более осмысленными - Заполнители в переводах стали более осмысленными.
- Изменена компоновка диалога добавления игры для лучшего отображения в Gamescope - Изменена компоновка диалога добавления игры для лучшего отображения в Gamescope.
- Текст бейджей теперь обрезается через ... если не помещается - Текст бейджей теперь обрезается троеточием, если не помещается.
### Fixed ### Fixed
- Дублирование обводки выделения карточек при быстром перемешении мыши - Дублирование обводки карточек при быстром перемещении мыши.
- Завершение приложения при закритие окна - Завершение приложения при закрытии окна.
- Использование системной палитры в темах - Использование системной палитры в темах.
- Ошибки темы в нативном пакете - Ошибки тем в нативном пакете.
- Ошибки темы в Gamescope - Ошибки тем в Gamescope.
- Размер иконок для desktop файлов теперь 128x128 - Размер иконок для desktop-файлов теперь 128x128.
- Пустая область при обновлении сетки игр - Пустая область при обновлении сетки игр.
- Запуск игры при открытом оверлее - Запуск игры при открытом оверлее.
### Contributors ### Contributors
- @Dervart - @Dervart
@@ -95,63 +106,63 @@
## [0.1.2] - 2025-06-15 ## [0.1.2] - 2025-06-15
### Added ### Added
- Кнопки сброса настроек и очистки кэша - Кнопки сброса настроек и очистки кэша.
- Бейдж PortProton - Бейдж PortProton.
- Зависимость от `xdg-utils` - Зависимость от `xdg-utils`.
- Интеграция статуса WeAntiCheatYet в карточку - Интеграция статуса WeAntiCheatYet в карточку.
- Переключение полноэкршанного режима через F11 или кнопку Select на геймпаде - Переключение полноэкранного режима через F11 или кнопку Select на геймпаде.
- Выбор состояния `QCheckBox` через Enter или кнопку A на геймпаде - Выбор состояния `QCheckBox` через Enter или кнопку A на геймпаде.
- Закрытие диалога добавления игры через ESC или кнопку B на геймпаде - Закрытие диалога добавления игры через ESC или кнопку B на геймпаде.
- Закрытие окна приложения по комбинации клавиш Ctrl+Q - Закрытие приложения комбинацией клавиш Ctrl+Q.
- Сохранение и восстановление размера окна при перезапуске - Сохранение и восстановление размера окна при перезапуске.
- Переключатель полноэкранного режима приложения - Переключатель полноэкранного режима приложения.
- Пункт в контекстном меню «Открыть папку игры» - Пункт в контекстном меню «Открыть папку игры».
- Пункты в контекстном меню «Добавить в Steam» и «Удалить из Steam» - Пункты в контекстном меню «Добавить в Steam» и «Удалить из Steam».
- Пункты в контекстном меню «Добавить в Избранное» и «Удалить из Избранного» - Пункты в контекстном меню «Добавить в избранное» и «Удалить из избранного».
- Метод сортировки «Сначала избранное» - Метод сортировки «Сначала избранное».
- Настройка автоматического перехода в полноэкранный режим при подключении геймпада (по умолчанию отключена) - Настройка автоматического перехода в полноэкранный режим при подключении геймпада (по умолчанию отключена).
- Поддержка управления геймпадом в `QMenu` и `QComboBox` - Поддержка управления геймпадом в `QMenu` и `QComboBox`.
- Аргумент `--fullscreen` для запуска приложения в полноэкранном режиме - Аргумент `--fullscreen` для запуска приложения в полноэкранном режиме.
- Оверлей на кнопку Insert или кнопку Xbox/PS на геймпаде для закрытия приложения, выключения, перезагрузки и перехода в спящий режим или переключения между сессиями - Оверлей на кнопку Insert или Xbox/PS-кнопку на геймпаде для закрытия приложения, выключения, перезагрузки, перехода в спящий режим или переключения между сессиями.
- [Gamescope сессия](https://git.linux-gaming.ru/Boria138/gamescope-session-portprotonqt) - [Gamescope-сессия](https://git.linux-gaming.ru/Boria138/gamescope-session-portprotonqt).
- Пресеты управления для DualShock 4 и DualSense - Пресеты управления для DualShock 4 и DualSense.
- Настройка тактильной отдачи на геймпаде при запуске игры (по умолчанию выключена) - Настройка тактильной отдачи на геймпаде при запуске игры (по умолчанию отключена).
- Переводы пунктов настроек - Переводы пунктов настроек.
### Changed ### Changed
- Обновлены все иконки - Обновлены все иконки.
- Переименована функция `_get_steam_home` в `get_steam_home` - Функция `_get_steam_home` переименована в `get_steam_home`.
- Переименован `steam_game` в `game_source` - `steam_game` переименован в `game_source`.
- Логика контекстного меню вынесена в `ContextMenuManager` - Логика контекстного меню вынесена в `ContextMenuManager`.
- Бейдж Steam теперь открывает Steam Community - Бейдж Steam теперь открывает Steam Community.
- Изменена лицензия с MIT на GPL-3.0 для совместимости с кодом от legendary - Лицензия изменена с MIT на GPL-3.0 для совместимости с кодом legendary.
- Оптимизирована генерация карточек для плавной работы при поиске и изменении размера окна - Оптимизирована генерация карточек для плавной работы при поиске и изменении размера окна.
- Бейджи с карточек теперь отображаются также на странице с деталями, а не только в библиотеке - Бейджи с карточек теперь отображаются и на странице с деталями, а не только в библиотеке.
- Установлена ширина бейджа в две трети ширины карточки - Установлена ширина бейджа в 2/3 ширины карточки.
- Бейджи источников (`Steam`, `EGS`, `PortProton`) теперь отображаются только при активном фильтре `all` или `favorites` - Бейджи источников (`Steam`, `EGS`, `PortProton`) отображаются только при активном фильтре `all` или `favorites`.
- Карточки теперь фокусируются в направлении движения стрелок или D-pad: - Карточки теперь фокусируются в направлении движения стрелок или D-pad.
- Поддерживается удержание D-pad для непрерывного переключения карточек - Поддерживается удержание D-pad для непрерывного переключения карточек.
- Объединён обработчик управления стрелками клавиатуры и D-pad для консистентности - Объединён обработчик управления стрелками клавиатуры и D-pad для консистентности.
- D-pad больше не переключает вкладки (только кнопки RB/LB) - D-pad больше не переключает вкладки (только кнопки RB/LB).
- Кнопка добавления игры больше не фокусируется - Кнопка добавления игры больше не получает фокус.
- Диалог добавления игры теперь открывается только в библиотеке - Диалог добавления игры открывается только в библиотеке.
- Удалены все упоминания PortProtonQT из кода и заменены на PortProtonQt - Все упоминания PortProtonQT заменены на PortProtonQt.
- Размер карточек теперь меняется только при отпускании слайдера - Размер карточек меняется только при отпускании слайдера.
- Слайдер теперь управляется через тригеры на геймпаде - Слайдер теперь управляется триггерами на геймпаде.
- Диалог добавления игры теперь открывается на X, а не на Y - Диалог добавления игры теперь открывается на X, а не на Y.
### Fixed ### Fixed
- Возврат к теме «standard» при выборе несуществующей темы - Возврат к теме «standard» при выборе несуществующей темы.
- Корректное открытие контекстного меню - Корректное открытие контекстного меню.
- Запуск приложения при отсутствии `exiftool` - Запуск приложения при отсутствии `exiftool`.
- Предотвращено бесконечное обращение к `get_portproton_location` - Предотвращено бесконечное обращение к `get_portproton_location`.
- Обновлены ссылки на документацию в README - Обновлены ссылки на документацию в README.
- Устранён traceback при отсутствии обложек (placeholder) - Исправлено падение при отсутствии обложек (placeholder).
- Устранены утечки памяти при загрузке обложек - Устранены утечки памяти при загрузке обложек.
- Исправлены ошибки при подключении геймпада - Исправлены ошибки при подключении геймпада.
- Предотвращено многократное открытие диалога добавления игры через геймпад - Предотвращено многократное открытие диалога добавления игры через геймпад.
- Корректная обработка событий геймпада во время игры - Корректная обработка событий геймпада во время игры.
- Убийсво всех процессов "зомби" при закрытии программы - Убийство всех процессов-зомби при закрытии программы.
### Contributors ### Contributors
- @Vector_null - @Vector_null
@@ -162,20 +173,20 @@
## [0.1.1] 2025-05-17 ## [0.1.1] 2025-05-17
### Added ### Added
- Алфавитная сортировка библиотеки - Алфавитная сортировка библиотеки.
- Проверка переводов через yaspeller - Проверка переводов через yaspeller.
- Сборка Fedora-пакета - Сборка Fedora-пакета.
- Сборка AppImage - Сборка AppImage.
### Changed ### Changed
- Удалён жёстко заданный размер окна - Удалён жёстко заданный размер окна.
- Использован `icoextract` как Python-модуль - Использован `icoextract` как Python-модуль.
### Fixed ### Fixed
- Скрытие статус-бара - Скрытие статус-бара.
- Чтение списка Steam-игр - Чтение списка Steam-игр.
- Зависание GUI - Зависание GUI.
- Сбой при повреждённом Steam - Сбой при повреждённом Steam.
### Contributors ### Contributors
- @Vector_null - @Vector_null

View File

@@ -21,9 +21,9 @@ Current translation status:
| Locale | Progress | Translated | | Locale | Progress | Translated |
| :----- | -------: | ---------: | | :----- | -------: | ---------: |
| [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 of 197 | | [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 of 195 |
| [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 of 197 | | [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 of 195 |
| [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 197 of 197 | | [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 195 of 195 |
--- ---

View File

@@ -21,9 +21,9 @@
| Локаль | Прогресс | Переведено | | Локаль | Прогресс | Переведено |
| :----- | -------: | ---------: | | :----- | -------: | ---------: |
| [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 из 197 | | [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 из 195 |
| [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 из 197 | | [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 из 195 |
| [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 197 из 197 | | [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 195 из 195 |
--- ---

View File

@@ -3,8 +3,7 @@ from PySide6.QtCore import QLocale, QTranslator, QLibraryInfo
from PySide6.QtWidgets import QApplication from PySide6.QtWidgets import QApplication
from PySide6.QtGui import QIcon from PySide6.QtGui import QIcon
from portprotonqt.main_window import MainWindow from portprotonqt.main_window import MainWindow
from portprotonqt.tray import SystemTray from portprotonqt.config_utils import save_fullscreen_config
from portprotonqt.config_utils import read_theme_from_config, save_fullscreen_config
from portprotonqt.logger import get_logger from portprotonqt.logger import get_logger
from portprotonqt.cli import parse_args from portprotonqt.cli import parse_args
@@ -38,35 +37,13 @@ def main():
save_fullscreen_config(True) save_fullscreen_config(True)
window.showFullScreen() window.showFullScreen()
current_theme_name = read_theme_from_config()
tray = SystemTray(app, current_theme_name)
tray.show_action.triggered.connect(window.show)
tray.hide_action.triggered.connect(window.hide)
def recreate_tray():
nonlocal tray
if tray:
logger.debug("Recreating system tray")
tray.cleanup()
tray = None
current_theme = read_theme_from_config()
tray = SystemTray(app, current_theme)
# Ensure window is not None before connecting signals
if window:
tray.show_action.triggered.connect(window.show)
tray.hide_action.triggered.connect(window.hide)
def cleanup_on_exit(): def cleanup_on_exit():
nonlocal tray, window nonlocal window
app.aboutToQuit.disconnect() app.aboutToQuit.disconnect()
if tray:
tray.cleanup()
tray = None
if window: if window:
window.close() window.close()
app.quit() app.quit()
window.settings_saved.connect(recreate_tray)
app.aboutToQuit.connect(cleanup_on_exit) app.aboutToQuit.connect(cleanup_on_exit)
window.show() window.show()

View File

@@ -549,3 +549,41 @@ def save_auto_fullscreen_gamepad(auto_fullscreen):
cp["Display"]["auto_fullscreen_gamepad"] = str(auto_fullscreen) cp["Display"]["auto_fullscreen_gamepad"] = str(auto_fullscreen)
with open(CONFIG_FILE, "w", encoding="utf-8") as configfile: with open(CONFIG_FILE, "w", encoding="utf-8") as configfile:
cp.write(configfile) cp.write(configfile)
def read_favorite_folders():
"""
Читает список избранных папок из секции [FavoritesFolders] конфигурационного файла.
Список хранится как строка, заключённая в кавычки, с путями, разделёнными запятыми.
Если секция или параметр отсутствуют, возвращает пустой список.
"""
cp = configparser.ConfigParser()
if os.path.exists(CONFIG_FILE):
try:
cp.read(CONFIG_FILE, encoding="utf-8")
except Exception as e:
logger.error("Ошибка чтения конфига: %s", e)
return []
if cp.has_section("FavoritesFolders") and cp.has_option("FavoritesFolders", "folders"):
favs = cp.get("FavoritesFolders", "folders", fallback="").strip()
if favs.startswith('"') and favs.endswith('"'):
favs = favs[1:-1]
return [os.path.normpath(s.strip()) for s in favs.split(",") if s.strip() and os.path.isdir(os.path.normpath(s.strip()))]
return []
def save_favorite_folders(folders):
"""
Сохраняет список избранных папок в секцию [FavoritesFolders] конфигурационного файла.
Список сохраняется как строка, заключённая в двойные кавычки, где пути разделены запятыми.
"""
cp = configparser.ConfigParser()
if os.path.exists(CONFIG_FILE):
try:
cp.read(CONFIG_FILE, encoding="utf-8")
except Exception as e:
logger.error("Ошибка чтения конфига: %s", e)
if "FavoritesFolders" not in cp:
cp["FavoritesFolders"] = {}
fav_str = ", ".join([os.path.normpath(folder) for folder in folders])
cp["FavoritesFolders"]["folders"] = f'"{fav_str}"'
with open(CONFIG_FILE, "w", encoding="utf-8") as configfile:
cp.write(configfile)

View File

@@ -12,7 +12,7 @@ from PySide6.QtWidgets import QMessageBox, QDialog, QMenu, QLineEdit, QApplicati
from PySide6.QtCore import QUrl, QPoint, QObject, Signal, Qt from PySide6.QtCore import QUrl, QPoint, QObject, Signal, Qt
from PySide6.QtGui import QDesktopServices, QIcon, QKeySequence from PySide6.QtGui import QDesktopServices, QIcon, QKeySequence
from portprotonqt.localization import _ from portprotonqt.localization import _
from portprotonqt.config_utils import parse_desktop_entry, read_favorites, save_favorites from portprotonqt.config_utils import parse_desktop_entry, read_favorites, save_favorites, read_favorite_folders, save_favorite_folders
from portprotonqt.steam_api import is_game_in_steam, add_to_steam, remove_from_steam from portprotonqt.steam_api import is_game_in_steam, add_to_steam, remove_from_steam
from portprotonqt.egs_api import add_egs_to_steam, get_egs_executable, remove_egs_from_steam from portprotonqt.egs_api import add_egs_to_steam, get_egs_executable, remove_egs_from_steam
from portprotonqt.dialogs import AddGameDialog, FileExplorer, generate_thumbnail from portprotonqt.dialogs import AddGameDialog, FileExplorer, generate_thumbnail
@@ -150,6 +150,84 @@ class ContextMenuManager:
return hasattr(self.parent, 'target_exe') and self.parent.target_exe == current_exe return hasattr(self.parent, 'target_exe') and self.parent.target_exe == current_exe
def show_folder_context_menu(self, file_explorer, pos):
"""Shows the context menu for a folder in FileExplorer."""
try:
item = file_explorer.file_list.itemAt(pos)
if not item:
logger.debug("No item selected at position %s", pos)
return
selected = item.text()
if not selected.endswith("/"):
logger.debug("Selected item is not a folder: %s", selected)
return # Only for folders
full_path = os.path.normpath(os.path.join(file_explorer.current_path, selected.rstrip("/")))
if not os.path.isdir(full_path):
logger.debug("Path is not a directory: %s", full_path)
return
menu = QMenu(file_explorer)
menu.setStyleSheet(self.theme.CONTEXT_MENU_STYLE)
menu.setParent(file_explorer, Qt.WindowType.Popup) # Set transientParent for Wayland
favorite_folders = read_favorite_folders()
is_favorite = full_path in favorite_folders
action_text = _("Remove from Favorites") if is_favorite else _("Add to Favorites")
favorite_action = menu.addAction(self._get_safe_icon("star" if is_favorite else "star_full"), action_text)
favorite_action.triggered.connect(lambda: self.toggle_favorite_folder(file_explorer, full_path, not is_favorite))
# Disconnect file_list signals to prevent navigation during menu interaction
try:
file_explorer.file_list.itemClicked.disconnect(file_explorer.handle_item_click)
file_explorer.file_list.itemDoubleClicked.disconnect(file_explorer.handle_item_double_click)
except TypeError:
pass # Signals may not be connected
# Reconnect signals after menu closes
def reconnect_signals():
try:
file_explorer.file_list.itemClicked.connect(file_explorer.handle_item_click)
file_explorer.file_list.itemDoubleClicked.connect(file_explorer.handle_item_double_click)
except Exception as e:
logger.error("Error reconnecting file list signals: %s", e)
menu.aboutToHide.connect(reconnect_signals)
# Set focus to the first menu item
actions = menu.actions()
if actions:
menu.setActiveAction(actions[0])
# Map local position to global for menu display
global_pos = file_explorer.file_list.mapToGlobal(pos)
menu.exec(global_pos)
except Exception as e:
logger.error("Error showing folder context menu: %s", e)
def toggle_favorite_folder(self, file_explorer, folder_path, add):
"""Adds or removes a folder from favorites."""
favorite_folders = read_favorite_folders()
if add:
if folder_path not in favorite_folders:
favorite_folders.append(folder_path)
save_favorite_folders(favorite_folders)
logger.info(f"Folder added to favorites: {folder_path}")
else:
if folder_path in favorite_folders:
favorite_folders.remove(folder_path)
save_favorite_folders(favorite_folders)
logger.info(f"Folder removed from favorites: {folder_path}")
file_explorer.update_drives_list()
def _get_safe_icon(self, icon_name: str) -> QIcon:
"""Returns a QIcon, ensuring it is valid."""
icon = self.theme_manager.get_icon(icon_name)
if isinstance(icon, QIcon):
return icon
elif isinstance(icon, str) and os.path.exists(icon):
return QIcon(icon)
return QIcon()
def show_context_menu(self, game_card, pos: QPoint): def show_context_menu(self, game_card, pos: QPoint):
""" """
Show the context menu for a game card at the specified position. Show the context menu for a game card at the specified position.
@@ -158,14 +236,6 @@ class ContextMenuManager:
game_card: The GameCard instance requesting the context menu. game_card: The GameCard instance requesting the context menu.
pos: The position (in widget coordinates) where the menu should appear. pos: The position (in widget coordinates) where the menu should appear.
""" """
def get_safe_icon(icon_name: str) -> QIcon:
icon = self.theme_manager.get_icon(icon_name)
if isinstance(icon, QIcon):
return icon
elif isinstance(icon, str) and os.path.exists(icon):
return QIcon(icon)
return QIcon()
menu = QMenu(self.parent) menu = QMenu(self.parent)
menu.setStyleSheet(self.theme.CONTEXT_MENU_STYLE) menu.setStyleSheet(self.theme.CONTEXT_MENU_STYLE)
@@ -175,7 +245,7 @@ class ContextMenuManager:
exe_path = self._parse_exe_path(exec_line, game_card.name) if exec_line else None exe_path = self._parse_exe_path(exec_line, game_card.name) if exec_line else None
if not exe_path: if not exe_path:
# Show only "Delete from PortProton" if no valid exe # Show only "Delete from PortProton" if no valid exe
delete_action = menu.addAction(get_safe_icon("delete"), _("Delete from PortProton")) delete_action = menu.addAction(self._get_safe_icon("delete"), _("Delete from PortProton"))
delete_action.triggered.connect(lambda: self.delete_game(game_card.name, game_card.exec_line)) delete_action.triggered.connect(lambda: self.delete_game(game_card.name, game_card.exec_line))
menu.exec(game_card.mapToGlobal(pos)) menu.exec(game_card.mapToGlobal(pos))
return return
@@ -184,7 +254,7 @@ class ContextMenuManager:
is_running = self._is_game_running(game_card) is_running = self._is_game_running(game_card)
action_text = _("Stop Game") if is_running else _("Launch Game") action_text = _("Stop Game") if is_running else _("Launch Game")
action_icon = "stop" if is_running else "play" action_icon = "stop" if is_running else "play"
launch_action = menu.addAction(get_safe_icon(action_icon), action_text) launch_action = menu.addAction(self._get_safe_icon(action_icon), action_text)
launch_action.triggered.connect( launch_action.triggered.connect(
lambda: self._launch_game(game_card) lambda: self._launch_game(game_card)
) )
@@ -193,11 +263,11 @@ class ContextMenuManager:
is_favorite = game_card.name in favorites is_favorite = game_card.name in favorites
icon_name = "star_full" if is_favorite else "star" icon_name = "star_full" if is_favorite else "star"
text = _("Remove from Favorites") if is_favorite else _("Add to Favorites") text = _("Remove from Favorites") if is_favorite else _("Add to Favorites")
favorite_action = menu.addAction(get_safe_icon(icon_name), text) favorite_action = menu.addAction(self._get_safe_icon(icon_name), text)
favorite_action.triggered.connect(lambda: self.toggle_favorite(game_card, not is_favorite)) favorite_action.triggered.connect(lambda: self.toggle_favorite(game_card, not is_favorite))
if game_card.game_source == "epic": if game_card.game_source == "epic":
import_action = menu.addAction(get_safe_icon("epic_games"), _("Import to Legendary")) import_action = menu.addAction(self._get_safe_icon("epic_games"), _("Import to Legendary"))
import_action.triggered.connect( import_action.triggered.connect(
lambda: self.import_to_legendary(game_card.name, game_card.appid) lambda: self.import_to_legendary(game_card.name, game_card.appid)
) )
@@ -205,13 +275,13 @@ class ContextMenuManager:
is_in_steam = is_game_in_steam(game_card.name) is_in_steam = is_game_in_steam(game_card.name)
icon_name = "delete" if is_in_steam else "steam" icon_name = "delete" if is_in_steam else "steam"
text = _("Remove from Steam") if is_in_steam else _("Add to Steam") text = _("Remove from Steam") if is_in_steam else _("Add to Steam")
steam_action = menu.addAction(get_safe_icon(icon_name), text) steam_action = menu.addAction(self._get_safe_icon(icon_name), text)
steam_action.triggered.connect( steam_action.triggered.connect(
lambda: self.remove_from_steam(game_card.name, game_card.exec_line, game_card.game_source) lambda: self.remove_from_steam(game_card.name, game_card.exec_line, game_card.game_source)
if is_in_steam if is_in_steam
else self.add_egs_to_steam(game_card.name, game_card.appid) else self.add_egs_to_steam(game_card.name, game_card.appid)
) )
open_folder_action = menu.addAction(get_safe_icon("search"), _("Open Game Folder")) open_folder_action = menu.addAction(self._get_safe_icon("search"), _("Open Game Folder"))
open_folder_action.triggered.connect( open_folder_action.triggered.connect(
lambda: self.open_egs_game_folder(game_card.appid) lambda: self.open_egs_game_folder(game_card.appid)
) )
@@ -219,7 +289,7 @@ class ContextMenuManager:
desktop_path = os.path.join(desktop_dir, f"{game_card.name}.desktop") desktop_path = os.path.join(desktop_dir, f"{game_card.name}.desktop")
icon_name = "delete" if os.path.exists(desktop_path) else "desktop" icon_name = "delete" if os.path.exists(desktop_path) else "desktop"
text = _("Remove from Desktop") if os.path.exists(desktop_path) else _("Add to Desktop") text = _("Remove from Desktop") if os.path.exists(desktop_path) else _("Add to Desktop")
desktop_action = menu.addAction(get_safe_icon(icon_name), text) desktop_action = menu.addAction(self._get_safe_icon(icon_name), text)
desktop_action.triggered.connect( desktop_action.triggered.connect(
lambda: self.remove_egs_from_desktop(game_card.name) lambda: self.remove_egs_from_desktop(game_card.name)
if os.path.exists(desktop_path) if os.path.exists(desktop_path)
@@ -228,7 +298,7 @@ class ContextMenuManager:
applications_dir = os.path.join(os.path.expanduser("~"), ".local", "share", "applications") applications_dir = os.path.join(os.path.expanduser("~"), ".local", "share", "applications")
menu_path = os.path.join(applications_dir, f"{game_card.name}.desktop") menu_path = os.path.join(applications_dir, f"{game_card.name}.desktop")
menu_action = menu.addAction( menu_action = menu.addAction(
get_safe_icon("delete" if os.path.exists(menu_path) else "menu"), self._get_safe_icon("delete" if os.path.exists(menu_path) else "menu"),
_("Remove from Menu") if os.path.exists(menu_path) else _("Add to Menu") _("Remove from Menu") if os.path.exists(menu_path) else _("Add to Menu")
) )
menu_action.triggered.connect( menu_action.triggered.connect(
@@ -242,19 +312,19 @@ class ContextMenuManager:
desktop_path = os.path.join(desktop_dir, f"{game_card.name}.desktop") desktop_path = os.path.join(desktop_dir, f"{game_card.name}.desktop")
icon_name = "delete" if os.path.exists(desktop_path) else "desktop" icon_name = "delete" if os.path.exists(desktop_path) else "desktop"
text = _("Remove from Desktop") if os.path.exists(desktop_path) else _("Add to Desktop") text = _("Remove from Desktop") if os.path.exists(desktop_path) else _("Add to Desktop")
desktop_action = menu.addAction(get_safe_icon(icon_name), text) desktop_action = menu.addAction(self._get_safe_icon(icon_name), text)
desktop_action.triggered.connect( desktop_action.triggered.connect(
lambda: self.remove_from_desktop(game_card.name) lambda: self.remove_from_desktop(game_card.name)
if os.path.exists(desktop_path) if os.path.exists(desktop_path)
else self.add_to_desktop(game_card.name, game_card.exec_line) else self.add_to_desktop(game_card.name, game_card.exec_line)
) )
edit_action = menu.addAction(get_safe_icon("edit"), _("Edit Shortcut")) edit_action = menu.addAction(self._get_safe_icon("edit"), _("Edit Shortcut"))
edit_action.triggered.connect( edit_action.triggered.connect(
lambda: self.edit_game_shortcut(game_card.name, game_card.exec_line, game_card.cover_path) lambda: self.edit_game_shortcut(game_card.name, game_card.exec_line, game_card.cover_path)
) )
delete_action = menu.addAction(get_safe_icon("delete"), _("Delete from PortProton")) delete_action = menu.addAction(self._get_safe_icon("delete"), _("Delete from PortProton"))
delete_action.triggered.connect(lambda: self.delete_game(game_card.name, game_card.exec_line)) delete_action.triggered.connect(lambda: self.delete_game(game_card.name, game_card.exec_line))
open_folder_action = menu.addAction(get_safe_icon("search"), _("Open Game Folder")) open_folder_action = menu.addAction(self._get_safe_icon("search"), _("Open Game Folder"))
open_folder_action.triggered.connect( open_folder_action.triggered.connect(
lambda: self.open_game_folder(game_card.name, game_card.exec_line) lambda: self.open_game_folder(game_card.name, game_card.exec_line)
) )
@@ -262,7 +332,7 @@ class ContextMenuManager:
menu_path = os.path.join(applications_dir, f"{game_card.name}.desktop") menu_path = os.path.join(applications_dir, f"{game_card.name}.desktop")
icon_name = "delete" if os.path.exists(menu_path) else "menu" icon_name = "delete" if os.path.exists(menu_path) else "menu"
text = _("Remove from Menu") if os.path.exists(menu_path) else _("Add to Menu") text = _("Remove from Menu") if os.path.exists(menu_path) else _("Add to Menu")
menu_action = menu.addAction(get_safe_icon(icon_name), text) menu_action = menu.addAction(self._get_safe_icon(icon_name), text)
menu_action.triggered.connect( menu_action.triggered.connect(
lambda: self.remove_from_menu(game_card.name) lambda: self.remove_from_menu(game_card.name)
if os.path.exists(menu_path) if os.path.exists(menu_path)
@@ -271,7 +341,7 @@ class ContextMenuManager:
is_in_steam = is_game_in_steam(game_card.name) is_in_steam = is_game_in_steam(game_card.name)
icon_name = "delete" if is_in_steam else "steam" icon_name = "delete" if is_in_steam else "steam"
text = _("Remove from Steam") if is_in_steam else _("Add to Steam") text = _("Remove from Steam") if is_in_steam else _("Add to Steam")
steam_action = menu.addAction(get_safe_icon(icon_name), text) steam_action = menu.addAction(self._get_safe_icon(icon_name), text)
steam_action.triggered.connect( steam_action.triggered.connect(
lambda: ( lambda: (
self.remove_from_steam(game_card.name, game_card.exec_line, game_card.game_source) self.remove_from_steam(game_card.name, game_card.exec_line, game_card.game_source)
@@ -280,7 +350,7 @@ class ContextMenuManager:
) )
) )
# Устанавливаем фокус на первый элемент меню # Set focus to the first menu item
actions = menu.actions() actions = menu.actions()
if actions: if actions:
menu.setActiveAction(actions[0]) menu.setActiveAction(actions[0])
@@ -422,7 +492,7 @@ class ContextMenuManager:
) )
return return
# Используем FileExplorer с directory_only=True # Use FileExplorer with directory_only=True
file_explorer = FileExplorer( file_explorer = FileExplorer(
parent=self.parent, parent=self.parent,
theme=self.theme, theme=self.theme,
@@ -452,10 +522,10 @@ class ContextMenuManager:
self._show_status_message(_("Importing '{game_name}' to Legendary...").format(game_name=game_name)) self._show_status_message(_("Importing '{game_name}' to Legendary...").format(game_name=game_name))
threading.Thread(target=run_import, daemon=True).start() threading.Thread(target=run_import, daemon=True).start()
# Подключаем сигнал выбора файла/папки # Connect the file selection signal
file_explorer.file_signal.file_selected.connect(on_folder_selected) file_explorer.file_signal.file_selected.connect(on_folder_selected)
# Центрируем FileExplorer относительно родительского виджета # Center FileExplorer relative to the parent widget
parent_widget = self.parent parent_widget = self.parent
if parent_widget: if parent_widget:
parent_geometry = parent_widget.geometry() parent_geometry = parent_widget.geometry()
@@ -789,7 +859,7 @@ Icon={icon_path}
_("Failed to delete custom data: {error}").format(error=str(e)) _("Failed to delete custom data: {error}").format(error=str(e))
) )
# Перезагрузка списка игр и обновление сетки # Reload games list and update grid
self.load_games() self.load_games()
self.update_game_grid() self.update_game_grid()

View File

@@ -9,7 +9,7 @@ from PySide6.QtWidgets import (
from PySide6.QtCore import Qt, QObject, Signal, QMimeDatabase, QTimer from PySide6.QtCore import Qt, QObject, Signal, QMimeDatabase, QTimer
from icoextract import IconExtractor, IconExtractorError from icoextract import IconExtractor, IconExtractorError
from PIL import Image from PIL import Image
from portprotonqt.config_utils import get_portproton_location from portprotonqt.config_utils import get_portproton_location, read_favorite_folders
from portprotonqt.localization import _ from portprotonqt.localization import _
from portprotonqt.logger import get_logger from portprotonqt.logger import get_logger
import portprotonqt.themes.standart.styles as default_styles import portprotonqt.themes.standart.styles as default_styles
@@ -106,13 +106,15 @@ class FileExplorer(QDialog):
self.setWindowModality(Qt.WindowModality.ApplicationModal) self.setWindowModality(Qt.WindowModality.ApplicationModal)
self.setWindowFlags(self.windowFlags() & ~Qt.WindowType.WindowContextHelpButtonHint) self.setWindowFlags(self.windowFlags() & ~Qt.WindowType.WindowContextHelpButtonHint)
# Find InputManager from parent # Find InputManager and ContextMenuManager from parent
self.input_manager = None self.input_manager = None
self.context_menu_manager = None
parent = self.parent() parent = self.parent()
while parent: while parent:
if hasattr(parent, 'input_manager'): if hasattr(parent, 'input_manager'):
self.input_manager = cast("MainWindow", parent).input_manager self.input_manager = cast("MainWindow", parent).input_manager
break if hasattr(parent, 'context_menu_manager'):
self.context_menu_manager = cast("MainWindow", parent).context_menu_manager
parent = parent.parent() parent = parent.parent()
if self.input_manager: if self.input_manager:
@@ -137,8 +139,9 @@ class FileExplorer(QDialog):
if len(parts) < 2: if len(parts) < 2:
continue continue
mount_point = parts[1] mount_point = parts[1]
# Исключаем системные и временные пути # Исключаем системные и временные пути, но сохраняем /run/media
if mount_point.startswith(('/run', '/dev', '/sys', '/proc', '/tmp', '/snap', '/var/lib')): if (mount_point.startswith(('/dev', '/sys', '/proc', '/tmp', '/snap', '/var/lib')) or
(mount_point.startswith('/run') and not mount_point.startswith('/run/media'))):
continue continue
# Проверяем, является ли точка монтирования директорией и доступна ли она # Проверяем, является ли точка монтирования директорией и доступна ли она
if os.path.isdir(mount_point) and os.access(mount_point, os.R_OK): if os.path.isdir(mount_point) and os.access(mount_point, os.R_OK):
@@ -150,7 +153,7 @@ class FileExplorer(QDialog):
def setup_ui(self): def setup_ui(self):
"""Настройка интерфейса""" """Настройка интерфейса"""
self.setWindowTitle("File Explorer") self.setWindowTitle(_("File Explorer"))
self.setGeometry(100, 100, 600, 600) self.setGeometry(100, 100, 600, 600)
self.main_layout = QVBoxLayout() self.main_layout = QVBoxLayout()
@@ -158,7 +161,7 @@ class FileExplorer(QDialog):
self.main_layout.setSpacing(10) self.main_layout.setSpacing(10)
self.setLayout(self.main_layout) self.setLayout(self.main_layout)
# Панель для смонтированных дисков # Панель для смонтированных дисков и избранных папок
self.drives_layout = QHBoxLayout() self.drives_layout = QHBoxLayout()
self.drives_scroll = QScrollArea() self.drives_scroll = QScrollArea()
self.drives_scroll.setWidgetResizable(True) self.drives_scroll.setWidgetResizable(True)
@@ -169,7 +172,7 @@ class FileExplorer(QDialog):
self.drives_scroll.setFixedHeight(70) self.drives_scroll.setFixedHeight(70)
self.main_layout.addWidget(self.drives_scroll) self.main_layout.addWidget(self.drives_scroll)
self.drives_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) self.drives_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.drives_scroll.setFocusPolicy(Qt.FocusPolicy.StrongFocus) # Allow focus on scroll area self.drives_scroll.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
# Путь # Путь
self.path_label = QLabel() self.path_label = QLabel()
@@ -181,6 +184,8 @@ class FileExplorer(QDialog):
self.file_list.setStyleSheet(self.theme.FILE_EXPLORER_STYLE) self.file_list.setStyleSheet(self.theme.FILE_EXPLORER_STYLE)
self.file_list.itemClicked.connect(self.handle_item_click) self.file_list.itemClicked.connect(self.handle_item_click)
self.file_list.itemDoubleClicked.connect(self.handle_item_double_click) self.file_list.itemDoubleClicked.connect(self.handle_item_double_click)
self.file_list.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.file_list.customContextMenuRequested.connect(self.show_folder_context_menu)
self.main_layout.addWidget(self.file_list) self.main_layout.addWidget(self.file_list)
# Кнопки # Кнопки
@@ -197,6 +202,13 @@ class FileExplorer(QDialog):
self.select_button.clicked.connect(self.select_item) self.select_button.clicked.connect(self.select_item)
self.cancel_button.clicked.connect(self.reject) self.cancel_button.clicked.connect(self.reject)
def show_folder_context_menu(self, pos):
"""Shows the context menu for a folder using ContextMenuManager."""
if self.context_menu_manager:
self.context_menu_manager.show_folder_context_menu(self, pos)
else:
logger.warning("ContextMenuManager not found in parent")
def move_selection(self, direction): def move_selection(self, direction):
"""Перемещение выбора по списку""" """Перемещение выбора по списку"""
current_row = self.file_list.currentRow() current_row = self.file_list.currentRow()
@@ -286,44 +298,96 @@ class FileExplorer(QDialog):
except Exception as e: except Exception as e:
logger.error(f"Error navigating to parent directory: {e}") logger.error(f"Error navigating to parent directory: {e}")
def ensure_button_visible(self, button):
"""Ensure the specified button is visible in the drives_scroll area."""
try:
if not button or not self.drives_scroll:
return
# Ensure the button is visible in the scroll area
self.drives_scroll.ensureWidgetVisible(button, 50, 50)
logger.debug(f"Ensured button {button.text()} is visible in drives_scroll")
except Exception as e:
logger.error(f"Error ensuring button visible: {e}")
def update_drives_list(self): def update_drives_list(self):
"""Обновление списка смонтированных дисков""" """Обновление списка смонтированных дисков и избранных папок."""
for i in reversed(range(self.drives_layout.count())): for i in reversed(range(self.drives_layout.count())):
widget = self.drives_layout.itemAt(i).widget() item = self.drives_layout.itemAt(i)
if widget: if item and item.widget():
widget = item.widget()
self.drives_layout.removeWidget(widget)
widget.deleteLater() widget.deleteLater()
self.drive_buttons = []
drives = self.get_mounted_drives() drives = self.get_mounted_drives()
self.drive_buttons = [] # Store buttons for navigation favorite_folders = read_favorite_folders()
# Добавляем смонтированные диски
for drive in drives: for drive in drives:
drive_name = os.path.basename(drive) or drive.split('/')[-1] or drive drive_name = os.path.basename(drive) or drive.split('/')[-1] or drive
button = AutoSizeButton(drive_name, icon=self.theme_manager.get_icon("mount_point")) button = AutoSizeButton(drive_name, icon=self.theme_manager.get_icon("mount_point"))
button.setStyleSheet(self.theme.ACTION_BUTTON_STYLE) button.setStyleSheet(self.theme.ACTION_BUTTON_STYLE)
button.setFocusPolicy(Qt.FocusPolicy.StrongFocus) # Make button focusable button.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
button.clicked.connect(lambda checked, path=drive: self.change_drive(path)) button.clicked.connect(lambda checked, path=drive: self.change_drive(path))
self.drives_layout.addWidget(button) self.drives_layout.addWidget(button)
self.drive_buttons.append(button) self.drive_buttons.append(button)
self.drives_layout.addStretch()
# Set focus to first drive button if available # Добавляем избранные папки
if self.drive_buttons: for folder in favorite_folders:
self.drive_buttons[0].setFocus() folder_name = os.path.basename(folder) or folder.split('/')[-1] or folder
button = AutoSizeButton(folder_name, icon=self.theme_manager.get_icon("folder"))
button.setStyleSheet(self.theme.ACTION_BUTTON_STYLE)
button.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
button.clicked.connect(lambda checked, path=folder: self.change_drive(path))
self.drives_layout.addWidget(button)
self.drive_buttons.append(button)
# Добавляем растяжку, чтобы выровнять элементы
spacer = QWidget()
spacer.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
self.drives_layout.addWidget(spacer)
def select_drive(self): def select_drive(self):
"""Handle drive selection via gamepad""" """Обрабатывает выбор диска или избранной папки через геймпад."""
focused_widget = QApplication.focusWidget() focused_widget = QApplication.focusWidget()
if isinstance(focused_widget, AutoSizeButton) and focused_widget in self.drive_buttons: if isinstance(focused_widget, AutoSizeButton) and focused_widget in self.drive_buttons:
drive_path = None drive_name = focused_widget.text().strip() # Удаляем пробелы
for drive in self.get_mounted_drives(): logger.debug(f"Выбрано имя: {drive_name}")
drive_name = os.path.basename(drive) or drive.split('/')[-1] or drive
if drive_name == focused_widget.text(): # Специальная обработка корневого каталога
drive_path = drive if drive_name == "/":
break if os.path.isdir("/") and os.access("/", os.R_OK):
if drive_path and os.path.isdir(drive_path) and os.access(drive_path, os.R_OK): self.current_path = "/"
self.current_path = os.path.normpath(drive_path)
self.update_file_list() self.update_file_list()
logger.info("Выбран корневой каталог: /")
return
else: else:
logger.warning(f"Путь диска недоступен: {drive_path}") logger.warning("Корневой каталог недоступен: недостаточно прав или ошибка пути")
return
# Проверяем избранные папки
favorite_folders = read_favorite_folders()
logger.debug(f"Избранные папки: {favorite_folders}")
for folder in favorite_folders:
folder_name = os.path.basename(os.path.normpath(folder)) or folder # Для корневых путей
if folder_name == drive_name and os.path.isdir(folder) and os.access(folder, os.R_OK):
self.current_path = os.path.normpath(folder)
self.update_file_list()
logger.info(f"Выбрана избранная папка: {self.current_path}")
return
# Проверяем смонтированные диски
mounted_drives = self.get_mounted_drives()
logger.debug(f"Смонтированные диски: {mounted_drives}")
for drive in mounted_drives:
drive_basename = os.path.basename(os.path.normpath(drive)) or drive # Для корневых путей
if drive_basename == drive_name and os.path.isdir(drive) and os.access(drive, os.R_OK):
self.current_path = os.path.normpath(drive)
self.update_file_list()
logger.info(f"Выбран смонтированный диск: {self.current_path}")
return
logger.warning(f"Путь недоступен: {drive_name}.")
def change_drive(self, drive_path): def change_drive(self, drive_path):
"""Переход к выбранному диску""" """Переход к выбранному диску"""

View File

@@ -188,7 +188,7 @@ class GameCard(QFrame):
self.egsLabel.setVisible(self.egs_visible) self.egsLabel.setVisible(self.egs_visible)
# PortProton бейдж # PortProton бейдж
portproton_icon = self.theme_manager.get_icon("ppqt-tray") portproton_icon = self.theme_manager.get_icon("portproton")
self.portprotonLabel = ClickableLabel( self.portprotonLabel = ClickableLabel(
"PortProton", "PortProton",
icon=portproton_icon, icon=portproton_icon,

View File

@@ -4,7 +4,7 @@ import os
from typing import Protocol, cast from typing import Protocol, cast
from evdev import InputDevice, InputEvent, ecodes, list_devices, ff from evdev import InputDevice, InputEvent, ecodes, list_devices, ff
from pyudev import Context, Monitor, MonitorObserver, Device from pyudev import Context, Monitor, MonitorObserver, Device
from PySide6.QtWidgets import QWidget, QStackedWidget, QApplication, QScrollArea, QLineEdit, QDialog, QMenu, QComboBox, QListView, QMessageBox from PySide6.QtWidgets import QWidget, QStackedWidget, QApplication, QScrollArea, QLineEdit, QDialog, QMenu, QComboBox, QListView, QMessageBox, QListWidget
from PySide6.QtCore import Qt, QObject, QEvent, QPoint, Signal, Slot, QTimer from PySide6.QtCore import Qt, QObject, QEvent, QPoint, Signal, Slot, QTimer
from PySide6.QtGui import QKeyEvent from PySide6.QtGui import QKeyEvent
from portprotonqt.logger import get_logger from portprotonqt.logger import get_logger
@@ -51,8 +51,8 @@ BUTTONS = {
'context_menu': {ecodes.BTN_START}, # Start (Xbox) / Options (PS) 'context_menu': {ecodes.BTN_START}, # Start (Xbox) / Options (PS)
'menu': {ecodes.BTN_SELECT}, # Select (Xbox) / Share (PS) 'menu': {ecodes.BTN_SELECT}, # Select (Xbox) / Share (PS)
'guide': {ecodes.BTN_MODE}, # Xbox Button / PS Button 'guide': {ecodes.BTN_MODE}, # Xbox Button / PS Button
'increase_size': {ecodes.ABS_RZ}, # RT (Xbox) / R2 (PS) 'increase_size': {ecodes.BTN_TR2}, # RT (Xbox) / R2 (PS)
'decrease_size': {ecodes.ABS_Z}, # LT (Xbox) / L2 (PS) 'decrease_size': {ecodes.BTN_TL2}, # LT (Xbox) / L2 (PS)
} }
class InputManager(QObject): class InputManager(QObject):
@@ -161,7 +161,20 @@ class InputManager(QObject):
def handle_file_explorer_button(self, button_code): def handle_file_explorer_button(self, button_code):
try: try:
popup = QApplication.activePopupWidget()
if isinstance(popup, QMenu):
if button_code in BUTTONS['confirm']: # A button (BTN_SOUTH)
if popup.activeAction():
popup.activeAction().trigger()
popup.close()
return
elif button_code in BUTTONS['back']: # B button
popup.close()
return
return # Skip other handling if menu is open
if not self.file_explorer or not hasattr(self.file_explorer, 'file_list'): if not self.file_explorer or not hasattr(self.file_explorer, 'file_list'):
logger.debug("No file explorer or file_list available")
return return
focused_widget = QApplication.focusWidget() focused_widget = QApplication.focusWidget()
@@ -169,27 +182,37 @@ class InputManager(QObject):
if isinstance(focused_widget, AutoSizeButton) and hasattr(self.file_explorer, 'drive_buttons') and focused_widget in self.file_explorer.drive_buttons: if isinstance(focused_widget, AutoSizeButton) and hasattr(self.file_explorer, 'drive_buttons') and focused_widget in self.file_explorer.drive_buttons:
self.file_explorer.select_drive() # Select the focused drive self.file_explorer.select_drive() # Select the focused drive
elif self.file_explorer.file_list.count() == 0: elif self.file_explorer.file_list.count() == 0:
logger.debug("File list is empty")
return return
else: else:
selected = self.file_explorer.file_list.currentItem().text() selected = self.file_explorer.file_list.currentItem().text()
full_path = os.path.join(self.file_explorer.current_path, selected) full_path = os.path.join(self.file_explorer.current_path, selected)
if os.path.isdir(full_path): if os.path.isdir(full_path):
# Открываем директорию
self.file_explorer.current_path = os.path.normpath(full_path) self.file_explorer.current_path = os.path.normpath(full_path)
self.file_explorer.update_file_list() self.file_explorer.update_file_list()
elif not self.file_explorer.directory_only: elif not self.file_explorer.directory_only:
# Выбираем файл, если directory_only=False
self.file_explorer.file_signal.file_selected.emit(os.path.normpath(full_path)) self.file_explorer.file_signal.file_selected.emit(os.path.normpath(full_path))
self.file_explorer.accept() self.file_explorer.accept()
else: else:
logger.debug("Selected item is not a directory, cannot select: %s", full_path) logger.debug("Selected item is not a directory, cannot select: %s", full_path)
elif button_code in BUTTONS['context_menu']: # Start button (BTN_START)
if self.file_explorer.file_list.count() == 0:
logger.debug("File list is empty, cannot show context menu")
return
current_item = self.file_explorer.file_list.currentItem()
if current_item:
item_rect = self.file_explorer.file_list.visualItemRect(current_item)
pos = item_rect.center() # Use local coordinates for itemAt check
self.file_explorer.show_folder_context_menu(pos)
else:
logger.debug("No item selected for context menu")
elif button_code in BUTTONS['add_game']: # X button elif button_code in BUTTONS['add_game']: # X button
if self.file_explorer.file_list.count() == 0: if self.file_explorer.file_list.count() == 0:
logger.debug("File list is empty")
return return
selected = self.file_explorer.file_list.currentItem().text() selected = self.file_explorer.file_list.currentItem().text()
full_path = os.path.join(self.file_explorer.current_path, selected) full_path = os.path.join(self.file_explorer.current_path, selected)
if os.path.isdir(full_path): if os.path.isdir(full_path):
# Подтверждаем выбор директории
self.file_explorer.file_signal.file_selected.emit(os.path.normpath(full_path)) self.file_explorer.file_signal.file_selected.emit(os.path.normpath(full_path))
self.file_explorer.accept() self.file_explorer.accept()
else: else:
@@ -202,12 +225,29 @@ class InputManager(QObject):
if self.original_button_handler: if self.original_button_handler:
self.original_button_handler(button_code) self.original_button_handler(button_code)
except Exception as e: except Exception as e:
logger.error(f"Error in FileExplorer button handler: {e}") logger.error("Error in FileExplorer button handler: %s", e)
def handle_file_explorer_dpad(self, code, value, current_time): def handle_file_explorer_dpad(self, code, value, current_time):
"""Обработка движения D-pad и левого стика для FileExplorer""" """Обработка движения D-pad и левого стика для FileExplorer"""
try: try:
popup = QApplication.activePopupWidget()
if isinstance(popup, QMenu):
if code == ecodes.ABS_HAT0Y and value != 0:
actions = popup.actions()
if not actions:
return
current_action = popup.activeAction()
current_idx = actions.index(current_action) if current_action in actions else -1
if value > 0: # Down
next_idx = (current_idx + 1) % len(actions) if current_idx != -1 else 0
popup.setActiveAction(actions[next_idx])
elif value < 0: # Up
next_idx = (current_idx - 1) % len(actions) if current_idx != -1 else len(actions) - 1
popup.setActiveAction(actions[next_idx])
return # Skip other handling if menu is open
if not self.file_explorer or not hasattr(self.file_explorer, 'file_list') or not self.file_explorer.file_list: if not self.file_explorer or not hasattr(self.file_explorer, 'file_list') or not self.file_explorer.file_list:
logger.debug("No file explorer or file_list available")
return return
focused_widget = QApplication.focusWidget() focused_widget = QApplication.focusWidget()
@@ -216,14 +256,17 @@ class InputManager(QObject):
if not isinstance(focused_widget, AutoSizeButton) or focused_widget not in self.file_explorer.drive_buttons: if not isinstance(focused_widget, AutoSizeButton) or focused_widget not in self.file_explorer.drive_buttons:
# If not focused on a drive button, focus the first one # If not focused on a drive button, focus the first one
self.file_explorer.drive_buttons[0].setFocus() self.file_explorer.drive_buttons[0].setFocus()
self.file_explorer.ensure_button_visible(self.file_explorer.drive_buttons[0])
return return
current_idx = self.file_explorer.drive_buttons.index(focused_widget) current_idx = self.file_explorer.drive_buttons.index(focused_widget)
if value < 0: # Left if value < 0: # Left
next_idx = max(current_idx - 1, 0) next_idx = max(current_idx - 1, 0)
self.file_explorer.drive_buttons[next_idx].setFocus() self.file_explorer.drive_buttons[next_idx].setFocus()
self.file_explorer.ensure_button_visible(self.file_explorer.drive_buttons[next_idx])
elif value > 0: # Right elif value > 0: # Right
next_idx = min(current_idx + 1, len(self.file_explorer.drive_buttons) - 1) next_idx = min(current_idx + 1, len(self.file_explorer.drive_buttons) - 1)
self.file_explorer.drive_buttons[next_idx].setFocus() self.file_explorer.drive_buttons[next_idx].setFocus()
self.file_explorer.ensure_button_visible(self.file_explorer.drive_buttons[next_idx])
elif code in (ecodes.ABS_HAT0Y, ecodes.ABS_Y): elif code in (ecodes.ABS_HAT0Y, ecodes.ABS_Y):
if isinstance(focused_widget, AutoSizeButton) and focused_widget in self.file_explorer.drive_buttons: if isinstance(focused_widget, AutoSizeButton) and focused_widget in self.file_explorer.drive_buttons:
# Move focus to file list if navigating down from drive buttons # Move focus to file list if navigating down from drive buttons
@@ -264,7 +307,7 @@ class InputManager(QObject):
elif self.original_dpad_handler: elif self.original_dpad_handler:
self.original_dpad_handler(code, value, current_time) self.original_dpad_handler(code, value, current_time)
except Exception as e: except Exception as e:
logger.error(f"Error in FileExplorer dpad handler: {e}") logger.error("Error in FileExplorer dpad handler: %s", e)
def handle_navigation_repeat(self): def handle_navigation_repeat(self):
"""Плавное повторение движения с переменной скоростью для FileExplorer""" """Плавное повторение движения с переменной скоростью для FileExplorer"""
@@ -742,6 +785,11 @@ class InputManager(QObject):
if not app: if not app:
return super().eventFilter(obj, event) return super().eventFilter(obj, event)
# Ensure obj is a QObject
if not isinstance(obj, QObject):
logger.debug(f"Skipping event filter for non-QObject: {type(obj).__name__}")
return False
# Handle key press and release events # Handle key press and release events
if not isinstance(event, QKeyEvent): if not isinstance(event, QKeyEvent):
return super().eventFilter(obj, event) return super().eventFilter(obj, event)
@@ -754,6 +802,62 @@ class InputManager(QObject):
# Handle key press events # Handle key press events
if event.type() == QEvent.Type.KeyPress: if event.type() == QEvent.Type.KeyPress:
# Handle FileExplorer specific logic
if self.file_explorer:
# Handle drive buttons in FileExplorer
if key in (Qt.Key.Key_Return, Qt.Key.Key_Enter):
if isinstance(focused, AutoSizeButton) and hasattr(self.file_explorer, 'drive_buttons') and focused in self.file_explorer.drive_buttons:
self.file_explorer.select_drive()
return True
elif isinstance(focused, QListWidget) and focused == self.file_explorer.file_list:
current_item = focused.currentItem()
if current_item:
selected = current_item.text()
full_path = os.path.join(self.file_explorer.current_path, selected)
if os.path.isdir(full_path):
if selected == "../":
self.file_explorer.previous_dir()
else:
self.file_explorer.current_path = os.path.normpath(full_path)
self.file_explorer.update_file_list()
elif not self.file_explorer.directory_only:
self.file_explorer.file_signal.file_selected.emit(os.path.normpath(full_path))
self.file_explorer.accept()
return True
else:
self._parent.activateFocusedWidget()
return True
# Handle FileExplorer navigation with right arrow key
if key == Qt.Key.Key_Right:
try:
if hasattr(self.file_explorer, 'drive_buttons') and self.file_explorer.drive_buttons:
if not isinstance(focused, AutoSizeButton) or focused not in self.file_explorer.drive_buttons:
self.file_explorer.drive_buttons[0].setFocus()
self.file_explorer.ensure_button_visible(self.file_explorer.drive_buttons[0])
else:
current_idx = self.file_explorer.drive_buttons.index(focused)
next_idx = min(current_idx + 1, len(self.file_explorer.drive_buttons) - 1)
self.file_explorer.drive_buttons[next_idx].setFocus()
self.file_explorer.ensure_button_visible(self.file_explorer.drive_buttons[next_idx])
return True
except Exception as e:
logger.error(f"Error handling right arrow in FileExplorer: {e}")
return True
# Handle Backspace for FileExplorer navigation
if key == Qt.Key.Key_Backspace:
self.file_explorer.previous_dir()
return True
# Handle QLineEdit cursor movement with Left/Right arrows
if isinstance(focused, QLineEdit) and key in (Qt.Key.Key_Left, Qt.Key.Key_Right):
if key == Qt.Key.Key_Left:
focused.cursorBackward(False, 1) # Move cursor left by one character
elif key == Qt.Key.Key_Right:
focused.cursorForward(False, 1) # Move cursor right by one character
return True # Consume the event to prevent further processing
# Open system overlay with Insert # Open system overlay with Insert
if key == Qt.Key.Key_Insert: if key == Qt.Key.Key_Insert:
if not popup and not isinstance(active_win, QDialog): if not popup and not isinstance(active_win, QDialog):
@@ -765,9 +869,17 @@ class InputManager(QObject):
app.quit() app.quit()
return True return True
# Close AddGameDialog with Escape # Handle Backspace for FileExplorer navigation (move to parent directory)
if key == Qt.Key.Key_Escape and isinstance(popup, QDialog): if key == Qt.Key.Key_Backspace and self.file_explorer:
popup.reject() self.file_explorer.previous_dir()
return True
# Close Dialogs with Escape
if key == Qt.Key.Key_Escape:
if isinstance(focused, QLineEdit):
return False
if isinstance(active_win, QDialog):
active_win.reject()
return True return True
# FullscreenDialog navigation # FullscreenDialog navigation
@@ -783,8 +895,8 @@ class InputManager(QObject):
active_win.show_next() active_win.show_next()
return True # Consume event to prevent tab switching return True # Consume event to prevent tab switching
# Handle tab switching with Left/Right arrow keys when not in GameCard focus # Handle tab switching with Left/Right arrow keys when not in GameCard focus or QLineEdit
if key in (Qt.Key.Key_Left, Qt.Key.Key_Right) and (not isinstance(focused, GameCard) or focused is None): if key in (Qt.Key.Key_Left, Qt.Key.Key_Right) and not isinstance(focused, GameCard | QLineEdit) and not self.file_explorer:
idx = self._parent.stackedWidget.currentIndex() idx = self._parent.stackedWidget.currentIndex()
total = len(self._parent.tabButtons) total = len(self._parent.tabButtons)
if key == Qt.Key.Key_Left: if key == Qt.Key.Key_Left:

View File

@@ -9,7 +9,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-07-14 13:16+0500\n" "POT-Creation-Date: 2025-08-23 20:35+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de_DE\n" "Language: de_DE\n"
@@ -26,6 +26,9 @@ msgstr ""
msgid "PortProton is not found" msgid "PortProton is not found"
msgstr "" msgstr ""
msgid "Delete from PortProton"
msgstr ""
msgid "Stop Game" msgid "Stop Game"
msgstr "" msgstr ""
@@ -65,9 +68,6 @@ msgstr ""
msgid "Edit Shortcut" msgid "Edit Shortcut"
msgstr "" msgstr ""
msgid "Delete from PortProton"
msgstr ""
#, python-brace-format #, python-brace-format
msgid "Stopped '{game_name}'" msgid "Stopped '{game_name}'"
msgstr "" msgstr ""
@@ -170,18 +170,6 @@ msgstr ""
msgid "No .desktop file found for '{game_name}'" msgid "No .desktop file found for '{game_name}'"
msgstr "" msgstr ""
#, python-brace-format
msgid "Invalid executable command: {exec_line}"
msgstr ""
#, python-brace-format
msgid "Executable not found: {path}"
msgstr ""
#, python-brace-format
msgid "Failed to parse executable: {error}"
msgstr ""
msgid "Confirm Deletion" msgid "Confirm Deletion"
msgstr "" msgstr ""
@@ -260,6 +248,9 @@ msgstr ""
msgid "Select All" msgid "Select All"
msgstr "" msgstr ""
msgid "File Explorer"
msgstr ""
msgid "Select" msgid "Select"
msgstr "" msgstr ""

View File

@@ -9,7 +9,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-07-14 13:16+0500\n" "POT-Creation-Date: 2025-08-23 20:35+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: es_ES\n" "Language: es_ES\n"
@@ -26,6 +26,9 @@ msgstr ""
msgid "PortProton is not found" msgid "PortProton is not found"
msgstr "" msgstr ""
msgid "Delete from PortProton"
msgstr ""
msgid "Stop Game" msgid "Stop Game"
msgstr "" msgstr ""
@@ -65,9 +68,6 @@ msgstr ""
msgid "Edit Shortcut" msgid "Edit Shortcut"
msgstr "" msgstr ""
msgid "Delete from PortProton"
msgstr ""
#, python-brace-format #, python-brace-format
msgid "Stopped '{game_name}'" msgid "Stopped '{game_name}'"
msgstr "" msgstr ""
@@ -170,18 +170,6 @@ msgstr ""
msgid "No .desktop file found for '{game_name}'" msgid "No .desktop file found for '{game_name}'"
msgstr "" msgstr ""
#, python-brace-format
msgid "Invalid executable command: {exec_line}"
msgstr ""
#, python-brace-format
msgid "Executable not found: {path}"
msgstr ""
#, python-brace-format
msgid "Failed to parse executable: {error}"
msgstr ""
msgid "Confirm Deletion" msgid "Confirm Deletion"
msgstr "" msgstr ""
@@ -260,6 +248,9 @@ msgstr ""
msgid "Select All" msgid "Select All"
msgstr "" msgstr ""
msgid "File Explorer"
msgstr ""
msgid "Select" msgid "Select"
msgstr "" msgstr ""

View File

@@ -9,7 +9,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PortProtonQt 0.1.1\n" "Project-Id-Version: PortProtonQt 0.1.1\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-07-14 13:16+0500\n" "POT-Creation-Date: 2025-08-23 20:35+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -24,6 +24,9 @@ msgstr ""
msgid "PortProton is not found" msgid "PortProton is not found"
msgstr "" msgstr ""
msgid "Delete from PortProton"
msgstr ""
msgid "Stop Game" msgid "Stop Game"
msgstr "" msgstr ""
@@ -63,9 +66,6 @@ msgstr ""
msgid "Edit Shortcut" msgid "Edit Shortcut"
msgstr "" msgstr ""
msgid "Delete from PortProton"
msgstr ""
#, python-brace-format #, python-brace-format
msgid "Stopped '{game_name}'" msgid "Stopped '{game_name}'"
msgstr "" msgstr ""
@@ -168,18 +168,6 @@ msgstr ""
msgid "No .desktop file found for '{game_name}'" msgid "No .desktop file found for '{game_name}'"
msgstr "" msgstr ""
#, python-brace-format
msgid "Invalid executable command: {exec_line}"
msgstr ""
#, python-brace-format
msgid "Executable not found: {path}"
msgstr ""
#, python-brace-format
msgid "Failed to parse executable: {error}"
msgstr ""
msgid "Confirm Deletion" msgid "Confirm Deletion"
msgstr "" msgstr ""
@@ -258,6 +246,9 @@ msgstr ""
msgid "Select All" msgid "Select All"
msgstr "" msgstr ""
msgid "File Explorer"
msgstr ""
msgid "Select" msgid "Select"
msgstr "" msgstr ""

View File

@@ -9,8 +9,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-07-14 13:16+0500\n" "POT-Creation-Date: 2025-08-23 20:35+0500\n"
"PO-Revision-Date: 2025-07-14 13:16+0500\n" "PO-Revision-Date: 2025-08-23 20:35+0500\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language: ru_RU\n" "Language: ru_RU\n"
"Language-Team: ru_RU <LL@li.org>\n" "Language-Team: ru_RU <LL@li.org>\n"
@@ -27,6 +27,9 @@ msgstr "Ошибка"
msgid "PortProton is not found" msgid "PortProton is not found"
msgstr "PortProton не найден" msgstr "PortProton не найден"
msgid "Delete from PortProton"
msgstr "Удалить из PortProton"
msgid "Stop Game" msgid "Stop Game"
msgstr "Остановить игру" msgstr "Остановить игру"
@@ -66,9 +69,6 @@ msgstr "Добавить в меню"
msgid "Edit Shortcut" msgid "Edit Shortcut"
msgstr "Редактировать" msgstr "Редактировать"
msgid "Delete from PortProton"
msgstr "Удалить из PortProton"
#, python-brace-format #, python-brace-format
msgid "Stopped '{game_name}'" msgid "Stopped '{game_name}'"
msgstr "Остановлен(а) '{game_name}'" msgstr "Остановлен(а) '{game_name}'"
@@ -173,18 +173,6 @@ msgstr "Не удалось прочитать файл .desktop: {error}"
msgid "No .desktop file found for '{game_name}'" msgid "No .desktop file found for '{game_name}'"
msgstr "Файл .desktop для '{game_name}' не найден" msgstr "Файл .desktop для '{game_name}' не найден"
#, python-brace-format
msgid "Invalid executable command: {exec_line}"
msgstr "Недопустимая исполняемая команда: {exec_line}"
#, python-brace-format
msgid "Executable not found: {path}"
msgstr "Исполняемый файл не найден: {path}"
#, python-brace-format
msgid "Failed to parse executable: {error}"
msgstr "Не удалось разобрать исполняемый файл: {error}"
msgid "Confirm Deletion" msgid "Confirm Deletion"
msgstr "Подтвердите удаление" msgstr "Подтвердите удаление"
@@ -267,6 +255,9 @@ msgstr "Удалить"
msgid "Select All" msgid "Select All"
msgstr "Выбрать всё" msgstr "Выбрать всё"
msgid "File Explorer"
msgstr "Проводник"
msgid "Select" msgid "Select"
msgstr "Выбрать" msgstr "Выбрать"

View File

@@ -48,7 +48,6 @@ logger = get_logger(__name__)
class MainWindow(QMainWindow): class MainWindow(QMainWindow):
"""Main window of PortProtonQt.""" """Main window of PortProtonQt."""
settings_saved = Signal()
games_loaded = Signal(list) games_loaded = Signal(list)
update_progress = Signal(int) # Signal to update progress bar update_progress = Signal(int) # Signal to update progress bar
update_status_message = Signal(str, int) # Signal to update status message update_status_message = Signal(str, int) # Signal to update status message
@@ -1331,7 +1330,6 @@ class MainWindow(QMainWindow):
self.settingsDebounceTimer.start() self.settingsDebounceTimer.start()
self.settings_saved.emit()
# Управление полноэкранным режимом # Управление полноэкранным режимом
gamepad_connected = self.input_manager.find_gamepad() is not None gamepad_connected = self.input_manager.find_gamepad() is not None
@@ -1676,7 +1674,7 @@ class MainWindow(QMainWindow):
egsLabel.setVisible(egs_visible) egsLabel.setVisible(egs_visible)
# PortProton badge # PortProton badge
portproton_icon = self.theme_manager.get_icon("ppqt-tray") portproton_icon = self.theme_manager.get_icon("portproton")
portprotonLabel = ClickableLabel( portprotonLabel = ClickableLabel(
"PortProton", "PortProton",
icon=portproton_icon, icon=portproton_icon,

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -1,49 +0,0 @@
from PySide6.QtGui import QAction, QIcon
from PySide6.QtWidgets import QSystemTrayIcon, QMenu
from portprotonqt.theme_manager import ThemeManager
from typing import cast
import portprotonqt.themes.standart.styles as default_styles
from portprotonqt.config_utils import read_theme_from_config
class SystemTray:
def __init__(self, app, theme=None):
self.app = app
self.theme_manager = ThemeManager()
self.theme = theme if theme is not None else default_styles
self.current_theme_name = read_theme_from_config()
self.tray = QSystemTrayIcon()
self.tray.setIcon(cast(QIcon, self.theme_manager.get_icon("ppqt-tray", self.current_theme_name)))
self.tray.setToolTip("PortProtonQt")
self.tray.setVisible(True)
# Создаём меню
self.menu = QMenu()
self.hide_action = QAction("Скрыть окно")
self.menu.addAction(self.hide_action)
self.show_action = QAction("Показать окно")
self.menu.addAction(self.show_action)
self.quit_action = QAction("Выход")
self.quit_action.triggered.connect(app.quit)
self.menu.addAction(self.quit_action)
self.tray.setContextMenu(self.menu)
def hide_tray(self):
"""Скрыть иконку трея"""
if self.tray:
self.tray.setVisible(False)
if self.menu:
self.menu.deleteLater()
self.menu = None
def cleanup(self):
"""Очистка ресурсов трея"""
if self.tray:
self.tray.setVisible(False)
self.tray = None
if self.menu:
self.menu.deleteLater()
self.menu = None

View File

@@ -27,7 +27,7 @@ classifiers = [
requires-python = ">=3.10" requires-python = ">=3.10"
dependencies = [ dependencies = [
"babel>=2.17.0", "babel>=2.17.0",
"beautifulsoup4>=4.13.4", "beautifulsoup4>=4.13.5",
"evdev>=1.9.2", "evdev>=1.9.2",
"icoextract>=0.2.0", "icoextract>=0.2.0",
"numpy>=2.2.4", "numpy>=2.2.4",
@@ -36,7 +36,7 @@ dependencies = [
"psutil>=7.0.0", "psutil>=7.0.0",
"pyside6>=6.9.1", "pyside6>=6.9.1",
"pyudev>=0.24.3", "pyudev>=0.24.3",
"requests>=2.32.4", "requests>=2.32.5",
"tqdm>=4.67.1", "tqdm>=4.67.1",
"vdf>=3.4", "vdf>=3.4",
"websocket-client>=1.8.0", "websocket-client>=1.8.0",
@@ -105,5 +105,5 @@ ignore = [
dev = [ dev = [
"pre-commit>=4.3.0", "pre-commit>=4.3.0",
"pyaspeller>=2.0.2", "pyaspeller>=2.0.2",
"pyright>=1.1.403", "pyright>=1.1.404",
] ]

View File

@@ -19,7 +19,7 @@
"enabled": false "enabled": false
}, },
{ {
"matchManagers": ["github-actions", "pre-commit"], "matchManagers": ["github-actions", "pre-commit", "poetry"],
"enabled": false "enabled": false
}, },
{ {

320
uv.lock generated
View File

@@ -304,68 +304,79 @@ wheels = [
[[package]] [[package]]
name = "orjson" name = "orjson"
version = "3.10.18" version = "3.11.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/81/0b/fea456a3ffe74e70ba30e01ec183a9b26bec4d497f61dcfce1b601059c60/orjson-3.10.18.tar.gz", hash = "sha256:e8da3947d92123eda795b68228cafe2724815621fe35e8e320a9e9593a4bcd53", size = 5422810 } sdist = { url = "https://files.pythonhosted.org/packages/df/1d/5e0ae38788bdf0721326695e65fdf41405ed535f633eb0df0f06f57552fa/orjson-3.11.2.tar.gz", hash = "sha256:91bdcf5e69a8fd8e8bdb3de32b31ff01d2bd60c1e8d5fe7d5afabdcf19920309", size = 5470739 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/27/16/2ceb9fb7bc2b11b1e4a3ea27794256e93dee2309ebe297fd131a778cd150/orjson-3.10.18-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a45e5d68066b408e4bc383b6e4ef05e717c65219a9e1390abc6155a520cac402", size = 248927 }, { url = "https://files.pythonhosted.org/packages/a1/7b/7aebe925c6b1c46c8606a960fe1d6b681fccd4aaf3f37cd647c3309d6582/orjson-3.11.2-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d6b8a78c33496230a60dc9487118c284c15ebdf6724386057239641e1eb69761", size = 226896 },
{ url = "https://files.pythonhosted.org/packages/3d/e1/d3c0a2bba5b9906badd121da449295062b289236c39c3a7801f92c4682b0/orjson-3.10.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be3b9b143e8b9db05368b13b04c84d37544ec85bb97237b3a923f076265ec89c", size = 136995 }, { url = "https://files.pythonhosted.org/packages/7d/39/c952c9b0d51063e808117dd1e53668a2e4325cc63cfe7df453d853ee8680/orjson-3.11.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc04036eeae11ad4180d1f7b5faddb5dab1dee49ecd147cd431523869514873b", size = 111845 },
{ url = "https://files.pythonhosted.org/packages/d7/51/698dd65e94f153ee5ecb2586c89702c9e9d12f165a63e74eb9ea1299f4e1/orjson-3.10.18-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9b0aa09745e2c9b3bf779b096fa71d1cc2d801a604ef6dd79c8b1bfef52b2f92", size = 132893 }, { url = "https://files.pythonhosted.org/packages/f5/dc/90b7f29be38745eeacc30903b693f29fcc1097db0c2a19a71ffb3e9f2a5f/orjson-3.11.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9c04325839c5754c253ff301cee8aaed7442d974860a44447bb3be785c411c27", size = 116395 },
{ url = "https://files.pythonhosted.org/packages/b3/e5/155ce5a2c43a85e790fcf8b985400138ce5369f24ee6770378ee6b691036/orjson-3.10.18-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53a245c104d2792e65c8d225158f2b8262749ffe64bc7755b00024757d957a13", size = 137017 }, { url = "https://files.pythonhosted.org/packages/10/c2/fe84ba63164c22932b8d59b8810e2e58590105293a259e6dd1bfaf3422c9/orjson-3.11.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32769e04cd7fdc4a59854376211145a1bbbc0aea5e9d6c9755d3d3c301d7c0df", size = 118768 },
{ url = "https://files.pythonhosted.org/packages/46/bb/6141ec3beac3125c0b07375aee01b5124989907d61c72c7636136e4bd03e/orjson-3.10.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9495ab2611b7f8a0a8a505bcb0f0cbdb5469caafe17b0e404c3c746f9900469", size = 138290 }, { url = "https://files.pythonhosted.org/packages/a9/ce/d9748ec69b1a4c29b8e2bab8233e8c41c583c69f515b373f1fb00247d8c9/orjson-3.11.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ff285d14917ea1408a821786e3677c5261fa6095277410409c694b8e7720ae0", size = 120887 },
{ url = "https://files.pythonhosted.org/packages/77/36/6961eca0b66b7809d33c4ca58c6bd4c23a1b914fb23aba2fa2883f791434/orjson-3.10.18-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73be1cbcebadeabdbc468f82b087df435843c809cd079a565fb16f0f3b23238f", size = 142828 }, { url = "https://files.pythonhosted.org/packages/c1/66/b90fac8e4a76e83f981912d7f9524d402b31f6c1b8bff3e498aa321c326c/orjson-3.11.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2662f908114864b63ff75ffe6ffacf996418dd6cc25e02a72ad4bda81b1ec45a", size = 123650 },
{ url = "https://files.pythonhosted.org/packages/8b/2f/0c646d5fd689d3be94f4d83fa9435a6c4322c9b8533edbb3cd4bc8c5f69a/orjson-3.10.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8936ee2679e38903df158037a2f1c108129dee218975122e37847fb1d4ac68", size = 132806 }, { url = "https://files.pythonhosted.org/packages/33/81/56143898d1689c7f915ac67703efb97e8f2f8d5805ce8c2c3fd0f2bb6e3d/orjson-3.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab463cf5d08ad6623a4dac1badd20e88a5eb4b840050c4812c782e3149fe2334", size = 121287 },
{ url = "https://files.pythonhosted.org/packages/ea/af/65907b40c74ef4c3674ef2bcfa311c695eb934710459841b3c2da212215c/orjson-3.10.18-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7115fcbc8525c74e4c2b608129bef740198e9a120ae46184dac7683191042056", size = 135005 }, { url = "https://files.pythonhosted.org/packages/80/de/f9c6d00c127be766a3739d0d85b52a7c941e437d8dd4d573e03e98d0f89c/orjson-3.11.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:64414241bde943cbf3c00d45fcb5223dca6d9210148ba984aae6b5d63294502b", size = 119637 },
{ url = "https://files.pythonhosted.org/packages/c7/d1/68bd20ac6a32cd1f1b10d23e7cc58ee1e730e80624e3031d77067d7150fc/orjson-3.10.18-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:771474ad34c66bc4d1c01f645f150048030694ea5b2709b87d3bda273ffe505d", size = 413418 }, { url = "https://files.pythonhosted.org/packages/67/4c/ab70c7627022d395c1b4eb5badf6196b7144e82b46a3a17ed2354f9e592d/orjson-3.11.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:7773e71c0ae8c9660192ff144a3d69df89725325e3d0b6a6bb2c50e5ebaf9b84", size = 392478 },
{ url = "https://files.pythonhosted.org/packages/31/31/c701ec0bcc3e80e5cb6e319c628ef7b768aaa24b0f3b4c599df2eaacfa24/orjson-3.10.18-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c14047dbbea52886dd87169f21939af5d55143dad22d10db6a7514f058156a8", size = 153288 }, { url = "https://files.pythonhosted.org/packages/77/91/d890b873b69311db4fae2624c5603c437df9c857fb061e97706dac550a77/orjson-3.11.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:652ca14e283b13ece35bf3a86503c25592f294dbcfc5bb91b20a9c9a62a3d4be", size = 134343 },
{ url = "https://files.pythonhosted.org/packages/d9/31/5e1aa99a10893a43cfc58009f9da840990cc8a9ebb75aa452210ba18587e/orjson-3.10.18-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:641481b73baec8db14fdf58f8967e52dc8bda1f2aba3aa5f5c1b07ed6df50b7f", size = 137181 }, { url = "https://files.pythonhosted.org/packages/47/16/1aa248541b4830274a079c4aeb2aa5d1ff17c3f013b1d0d8d16d0848f3de/orjson-3.11.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:26e99e98df8990ecfe3772bbdd7361f602149715c2cbc82e61af89bfad9528a4", size = 123887 },
{ url = "https://files.pythonhosted.org/packages/bf/8c/daba0ac1b8690011d9242a0f37235f7d17df6d0ad941021048523b76674e/orjson-3.10.18-cp310-cp310-win32.whl", hash = "sha256:607eb3ae0909d47280c1fc657c4284c34b785bae371d007595633f4b1a2bbe06", size = 142694 }, { url = "https://files.pythonhosted.org/packages/95/e4/7419833c55ac8b5f385d00c02685a260da1f391e900fc5c3e0b797e0d506/orjson-3.11.2-cp310-cp310-win32.whl", hash = "sha256:5814313b3e75a2be7fe6c7958201c16c4560e21a813dbad25920752cecd6ad66", size = 124560 },
{ url = "https://files.pythonhosted.org/packages/16/62/8b687724143286b63e1d0fab3ad4214d54566d80b0ba9d67c26aaf28a2f8/orjson-3.10.18-cp310-cp310-win_amd64.whl", hash = "sha256:8770432524ce0eca50b7efc2a9a5f486ee0113a5fbb4231526d414e6254eba92", size = 134600 }, { url = "https://files.pythonhosted.org/packages/74/f8/27ca7ef3e194c462af32ce1883187f5ec483650c559166f0de59c4c2c5f0/orjson-3.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:dc471ce2225ab4c42ca672f70600d46a8b8e28e8d4e536088c1ccdb1d22b35ce", size = 119700 },
{ url = "https://files.pythonhosted.org/packages/97/c7/c54a948ce9a4278794f669a353551ce7db4ffb656c69a6e1f2264d563e50/orjson-3.10.18-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e0a183ac3b8e40471e8d843105da6fbe7c070faab023be3b08188ee3f85719b8", size = 248929 }, { url = "https://files.pythonhosted.org/packages/78/7d/e295df1ac9920cbb19fb4c1afa800e86f175cb657143aa422337270a4782/orjson-3.11.2-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:888b64ef7eaeeff63f773881929434a5834a6a140a63ad45183d59287f07fc6a", size = 226502 },
{ url = "https://files.pythonhosted.org/packages/9e/60/a9c674ef1dd8ab22b5b10f9300e7e70444d4e3cda4b8258d6c2488c32143/orjson-3.10.18-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5ef7c164d9174362f85238d0cd4afdeeb89d9e523e4651add6a5d458d6f7d42d", size = 133364 }, { url = "https://files.pythonhosted.org/packages/65/21/ffb0f10ea04caf418fb4e7ad1fda4b9ab3179df9d7a33b69420f191aadd5/orjson-3.11.2-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:83387cc8b26c9fa0ae34d1ea8861a7ae6cff8fb3e346ab53e987d085315a728e", size = 115999 },
{ url = "https://files.pythonhosted.org/packages/c1/4e/f7d1bdd983082216e414e6d7ef897b0c2957f99c545826c06f371d52337e/orjson-3.10.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd14c5d99cdc7bf93f22b12ec3b294931518aa019e2a147e8aa2f31fd3240f7", size = 136995 }, { url = "https://files.pythonhosted.org/packages/90/d5/8da1e252ac3353d92e6f754ee0c85027c8a2cda90b6899da2be0df3ef83d/orjson-3.11.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7e35f003692c216d7ee901b6b916b5734d6fc4180fcaa44c52081f974c08e17", size = 111563 },
{ url = "https://files.pythonhosted.org/packages/17/89/46b9181ba0ea251c9243b0c8ce29ff7c9796fa943806a9c8b02592fce8ea/orjson-3.10.18-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b672502323b6cd133c4af6b79e3bea36bad2d16bca6c1f645903fce83909a7a", size = 132894 }, { url = "https://files.pythonhosted.org/packages/4f/81/baabc32e52c570b0e4e1044b1bd2ccbec965e0de3ba2c13082255efa2006/orjson-3.11.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a0a4c29ae90b11d0c00bcc31533854d89f77bde2649ec602f512a7e16e00640", size = 116222 },
{ url = "https://files.pythonhosted.org/packages/ca/dd/7bce6fcc5b8c21aef59ba3c67f2166f0a1a9b0317dcca4a9d5bd7934ecfd/orjson-3.10.18-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51f8c63be6e070ec894c629186b1c0fe798662b8687f3d9fdfa5e401c6bd7679", size = 137016 }, { url = "https://files.pythonhosted.org/packages/8d/b7/da2ad55ad80b49b560dce894c961477d0e76811ee6e614b301de9f2f8728/orjson-3.11.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:585d712b1880f68370108bc5534a257b561672d1592fae54938738fe7f6f1e33", size = 118594 },
{ url = "https://files.pythonhosted.org/packages/1c/4a/b8aea1c83af805dcd31c1f03c95aabb3e19a016b2a4645dd822c5686e94d/orjson-3.10.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f9478ade5313d724e0495d167083c6f3be0dd2f1c9c8a38db9a9e912cdaf947", size = 138290 }, { url = "https://files.pythonhosted.org/packages/61/be/014f7eab51449f3c894aa9bbda2707b5340c85650cb7d0db4ec9ae280501/orjson-3.11.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d08e342a7143f8a7c11f1c4033efe81acbd3c98c68ba1b26b96080396019701f", size = 120700 },
{ url = "https://files.pythonhosted.org/packages/36/d6/7eb05c85d987b688707f45dcf83c91abc2251e0dd9fb4f7be96514f838b1/orjson-3.10.18-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:187aefa562300a9d382b4b4eb9694806e5848b0cedf52037bb5c228c61bb66d4", size = 142829 }, { url = "https://files.pythonhosted.org/packages/cf/ae/c217903a30c51341868e2d8c318c59a8413baa35af54d7845071c8ccd6fe/orjson-3.11.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c0f84fc50398773a702732c87cd622737bf11c0721e6db3041ac7802a686fb", size = 123433 },
{ url = "https://files.pythonhosted.org/packages/d2/78/ddd3ee7873f2b5f90f016bc04062713d567435c53ecc8783aab3a4d34915/orjson-3.10.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da552683bc9da222379c7a01779bddd0ad39dd699dd6300abaf43eadee38334", size = 132805 }, { url = "https://files.pythonhosted.org/packages/57/c2/b3c346f78b1ff2da310dd300cb0f5d32167f872b4d3bb1ad122c889d97b0/orjson-3.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:140f84e3c8d4c142575898c91e3981000afebf0333df753a90b3435d349a5fe5", size = 121061 },
{ url = "https://files.pythonhosted.org/packages/8c/09/c8e047f73d2c5d21ead9c180203e111cddeffc0848d5f0f974e346e21c8e/orjson-3.10.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e450885f7b47a0231979d9c49b567ed1c4e9f69240804621be87c40bc9d3cf17", size = 135008 }, { url = "https://files.pythonhosted.org/packages/00/c8/c97798f6010327ffc75ad21dd6bca11ea2067d1910777e798c2849f1c68f/orjson-3.11.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96304a2b7235e0f3f2d9363ddccdbfb027d27338722fe469fe656832a017602e", size = 119410 },
{ url = "https://files.pythonhosted.org/packages/0c/4b/dccbf5055ef8fb6eda542ab271955fc1f9bf0b941a058490293f8811122b/orjson-3.10.18-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5e3c9cc2ba324187cd06287ca24f65528f16dfc80add48dc99fa6c836bb3137e", size = 413419 }, { url = "https://files.pythonhosted.org/packages/37/fd/df720f7c0e35694617b7f95598b11a2cb0374661d8389703bea17217da53/orjson-3.11.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:3d7612bb227d5d9582f1f50a60bd55c64618fc22c4a32825d233a4f2771a428a", size = 392294 },
{ url = "https://files.pythonhosted.org/packages/8a/f3/1eac0c5e2d6d6790bd2025ebfbefcbd37f0d097103d76f9b3f9302af5a17/orjson-3.10.18-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:50ce016233ac4bfd843ac5471e232b865271d7d9d44cf9d33773bcd883ce442b", size = 153292 }, { url = "https://files.pythonhosted.org/packages/ba/52/0120d18f60ab0fe47531d520372b528a45c9a25dcab500f450374421881c/orjson-3.11.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a134587d18fe493befc2defffef2a8d27cfcada5696cb7234de54a21903ae89a", size = 134134 },
{ url = "https://files.pythonhosted.org/packages/1f/b4/ef0abf64c8f1fabf98791819ab502c2c8c1dc48b786646533a93637d8999/orjson-3.10.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b3ceff74a8f7ffde0b2785ca749fc4e80e4315c0fd887561144059fb1c138aa7", size = 137182 }, { url = "https://files.pythonhosted.org/packages/ec/10/1f967671966598366de42f07e92b0fc694ffc66eafa4b74131aeca84915f/orjson-3.11.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0b84455e60c4bc12c1e4cbaa5cfc1acdc7775a9da9cec040e17232f4b05458bd", size = 123745 },
{ url = "https://files.pythonhosted.org/packages/a9/a3/6ea878e7b4a0dc5c888d0370d7752dcb23f402747d10e2257478d69b5e63/orjson-3.10.18-cp311-cp311-win32.whl", hash = "sha256:fdba703c722bd868c04702cac4cb8c6b8ff137af2623bc0ddb3b3e6a2c8996c1", size = 142695 }, { url = "https://files.pythonhosted.org/packages/43/eb/76081238671461cfd0f47e0c24f408ffa66184237d56ef18c33e86abb612/orjson-3.11.2-cp311-cp311-win32.whl", hash = "sha256:f0660efeac223f0731a70884e6914a5f04d613b5ae500744c43f7bf7b78f00f9", size = 124393 },
{ url = "https://files.pythonhosted.org/packages/79/2a/4048700a3233d562f0e90d5572a849baa18ae4e5ce4c3ba6247e4ece57b0/orjson-3.10.18-cp311-cp311-win_amd64.whl", hash = "sha256:c28082933c71ff4bc6ccc82a454a2bffcef6e1d7379756ca567c772e4fb3278a", size = 134603 }, { url = "https://files.pythonhosted.org/packages/26/76/cc598c1811ba9ba935171267b02e377fc9177489efce525d478a2999d9cc/orjson-3.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:955811c8405251d9e09cbe8606ad8fdef49a451bcf5520095a5ed38c669223d8", size = 119561 },
{ url = "https://files.pythonhosted.org/packages/03/45/10d934535a4993d27e1c84f1810e79ccf8b1b7418cef12151a22fe9bb1e1/orjson-3.10.18-cp311-cp311-win_arm64.whl", hash = "sha256:a6c7c391beaedd3fa63206e5c2b7b554196f14debf1ec9deb54b5d279b1b46f5", size = 131400 }, { url = "https://files.pythonhosted.org/packages/d8/17/c48011750f0489006f7617b0a3cebc8230f36d11a34e7e9aca2085f07792/orjson-3.11.2-cp311-cp311-win_arm64.whl", hash = "sha256:2e4d423a6f838552e3a6d9ec734b729f61f88b1124fd697eab82805ea1a2a97d", size = 114186 },
{ url = "https://files.pythonhosted.org/packages/21/1a/67236da0916c1a192d5f4ccbe10ec495367a726996ceb7614eaa687112f2/orjson-3.10.18-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:50c15557afb7f6d63bc6d6348e0337a880a04eaa9cd7c9d569bcb4e760a24753", size = 249184 }, { url = "https://files.pythonhosted.org/packages/40/02/46054ebe7996a8adee9640dcad7d39d76c2000dc0377efa38e55dc5cbf78/orjson-3.11.2-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:901d80d349d8452162b3aa1afb82cec5bee79a10550660bc21311cc61a4c5486", size = 226528 },
{ url = "https://files.pythonhosted.org/packages/b3/bc/c7f1db3b1d094dc0c6c83ed16b161a16c214aaa77f311118a93f647b32dc/orjson-3.10.18-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:356b076f1662c9813d5fa56db7d63ccceef4c271b1fb3dd522aca291375fcf17", size = 133279 }, { url = "https://files.pythonhosted.org/packages/e2/c6/6b6f0b4d8aea1137436546b990f71be2cd8bd870aa2f5aa14dba0fcc95dc/orjson-3.11.2-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:cf3bd3967a360e87ee14ed82cb258b7f18c710dacf3822fb0042a14313a673a1", size = 115931 },
{ url = "https://files.pythonhosted.org/packages/af/84/664657cd14cc11f0d81e80e64766c7ba5c9b7fc1ec304117878cc1b4659c/orjson-3.10.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:559eb40a70a7494cd5beab2d73657262a74a2c59aff2068fdba8f0424ec5b39d", size = 136799 }, { url = "https://files.pythonhosted.org/packages/ae/05/4205cc97c30e82a293dd0d149b1a89b138ebe76afeca66fc129fa2aa4e6a/orjson-3.11.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26693dde66910078229a943e80eeb99fdce6cd2c26277dc80ead9f3ab97d2131", size = 111382 },
{ url = "https://files.pythonhosted.org/packages/9a/bb/f50039c5bb05a7ab024ed43ba25d0319e8722a0ac3babb0807e543349978/orjson-3.10.18-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3c29eb9a81e2fbc6fd7ddcfba3e101ba92eaff455b8d602bf7511088bbc0eae", size = 132791 }, { url = "https://files.pythonhosted.org/packages/50/c7/b8a951a93caa821f9272a7c917115d825ae2e4e8768f5ddf37968ec9de01/orjson-3.11.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ad4c8acb50a28211c33fc7ef85ddf5cb18d4636a5205fd3fa2dce0411a0e30c", size = 116271 },
{ url = "https://files.pythonhosted.org/packages/93/8c/ee74709fc072c3ee219784173ddfe46f699598a1723d9d49cbc78d66df65/orjson-3.10.18-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6612787e5b0756a171c7d81ba245ef63a3533a637c335aa7fcb8e665f4a0966f", size = 137059 }, { url = "https://files.pythonhosted.org/packages/17/03/1006c7f8782d5327439e26d9b0ec66500ea7b679d4bbb6b891d2834ab3ee/orjson-3.11.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:994181e7f1725bb5f2d481d7d228738e0743b16bf319ca85c29369c65913df14", size = 119086 },
{ url = "https://files.pythonhosted.org/packages/6a/37/e6d3109ee004296c80426b5a62b47bcadd96a3deab7443e56507823588c5/orjson-3.10.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ac6bd7be0dcab5b702c9d43d25e70eb456dfd2e119d512447468f6405b4a69c", size = 138359 }, { url = "https://files.pythonhosted.org/packages/44/61/57d22bc31f36a93878a6f772aea76b2184102c6993dea897656a66d18c74/orjson-3.11.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dbb79a0476393c07656b69c8e763c3cc925fa8e1d9e9b7d1f626901bb5025448", size = 120724 },
{ url = "https://files.pythonhosted.org/packages/4f/5d/387dafae0e4691857c62bd02839a3bf3fa648eebd26185adfac58d09f207/orjson-3.10.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9f72f100cee8dde70100406d5c1abba515a7df926d4ed81e20a9730c062fe9ad", size = 142853 }, { url = "https://files.pythonhosted.org/packages/78/a9/4550e96b4c490c83aea697d5347b8f7eb188152cd7b5a38001055ca5b379/orjson-3.11.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:191ed27a1dddb305083d8716af413d7219f40ec1d4c9b0e977453b4db0d6fb6c", size = 123577 },
{ url = "https://files.pythonhosted.org/packages/27/6f/875e8e282105350b9a5341c0222a13419758545ae32ad6e0fcf5f64d76aa/orjson-3.10.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dca85398d6d093dd41dc0983cbf54ab8e6afd1c547b6b8a311643917fbf4e0c", size = 133131 }, { url = "https://files.pythonhosted.org/packages/3a/86/09b8cb3ebd513d708ef0c92d36ac3eebda814c65c72137b0a82d6d688fc4/orjson-3.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0afb89f16f07220183fd00f5f297328ed0a68d8722ad1b0c8dcd95b12bc82804", size = 121195 },
{ url = "https://files.pythonhosted.org/packages/48/b2/73a1f0b4790dcb1e5a45f058f4f5dcadc8a85d90137b50d6bbc6afd0ae50/orjson-3.10.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22748de2a07fcc8781a70edb887abf801bb6142e6236123ff93d12d92db3d406", size = 134834 }, { url = "https://files.pythonhosted.org/packages/37/68/7b40b39ac2c1c644d4644e706d0de6c9999764341cd85f2a9393cb387661/orjson-3.11.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6ab6e6b4e93b1573a026b6ec16fca9541354dd58e514b62c558b58554ae04307", size = 119234 },
{ url = "https://files.pythonhosted.org/packages/56/f5/7ed133a5525add9c14dbdf17d011dd82206ca6840811d32ac52a35935d19/orjson-3.10.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3a83c9954a4107b9acd10291b7f12a6b29e35e8d43a414799906ea10e75438e6", size = 413368 }, { url = "https://files.pythonhosted.org/packages/40/7c/bb6e7267cd80c19023d44d8cbc4ea4ed5429fcd4a7eb9950f50305697a28/orjson-3.11.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9cb23527efb61fb75527df55d20ee47989c4ee34e01a9c98ee9ede232abf6219", size = 392250 },
{ url = "https://files.pythonhosted.org/packages/11/7c/439654221ed9c3324bbac7bdf94cf06a971206b7b62327f11a52544e4982/orjson-3.10.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:303565c67a6c7b1f194c94632a4a39918e067bd6176a48bec697393865ce4f06", size = 153359 }, { url = "https://files.pythonhosted.org/packages/64/f2/6730ace05583dbca7c1b406d59f4266e48cd0d360566e71482420fb849fc/orjson-3.11.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a4dd1268e4035af21b8a09e4adf2e61f87ee7bf63b86d7bb0a237ac03fad5b45", size = 134572 },
{ url = "https://files.pythonhosted.org/packages/48/e7/d58074fa0cc9dd29a8fa2a6c8d5deebdfd82c6cfef72b0e4277c4017563a/orjson-3.10.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:86314fdb5053a2f5a5d881f03fca0219bfdf832912aa88d18676a5175c6916b5", size = 137466 }, { url = "https://files.pythonhosted.org/packages/96/0f/7d3e03a30d5aac0432882b539a65b8c02cb6dd4221ddb893babf09c424cc/orjson-3.11.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ff8b155b145eaf5a9d94d2c476fbe18d6021de93cf36c2ae2c8c5b775763f14e", size = 123869 },
{ url = "https://files.pythonhosted.org/packages/57/4d/fe17581cf81fb70dfcef44e966aa4003360e4194d15a3f38cbffe873333a/orjson-3.10.18-cp312-cp312-win32.whl", hash = "sha256:187ec33bbec58c76dbd4066340067d9ece6e10067bb0cc074a21ae3300caa84e", size = 142683 }, { url = "https://files.pythonhosted.org/packages/45/80/1513265eba6d4a960f078f4b1d2bff94a571ab2d28c6f9835e03dfc65cc6/orjson-3.11.2-cp312-cp312-win32.whl", hash = "sha256:ae3bb10279d57872f9aba68c9931aa71ed3b295fa880f25e68da79e79453f46e", size = 124430 },
{ url = "https://files.pythonhosted.org/packages/e6/22/469f62d25ab5f0f3aee256ea732e72dc3aab6d73bac777bd6277955bceef/orjson-3.10.18-cp312-cp312-win_amd64.whl", hash = "sha256:f9f94cf6d3f9cd720d641f8399e390e7411487e493962213390d1ae45c7814fc", size = 134754 }, { url = "https://files.pythonhosted.org/packages/fb/61/eadf057b68a332351eeb3d89a4cc538d14f31cd8b5ec1b31a280426ccca2/orjson-3.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:d026e1967239ec11a2559b4146a61d13914504b396f74510a1c4d6b19dfd8732", size = 119598 },
{ url = "https://files.pythonhosted.org/packages/10/b0/1040c447fac5b91bc1e9c004b69ee50abb0c1ffd0d24406e1350c58a7fcb/orjson-3.10.18-cp312-cp312-win_arm64.whl", hash = "sha256:3d600be83fe4514944500fa8c2a0a77099025ec6482e8087d7659e891f23058a", size = 131218 }, { url = "https://files.pythonhosted.org/packages/6b/3f/7f4b783402143d965ab7e9a2fc116fdb887fe53bdce7d3523271cd106098/orjson-3.11.2-cp312-cp312-win_arm64.whl", hash = "sha256:59f8d5ad08602711af9589375be98477d70e1d102645430b5a7985fdbf613b36", size = 114052 },
{ url = "https://files.pythonhosted.org/packages/04/f0/8aedb6574b68096f3be8f74c0b56d36fd94bcf47e6c7ed47a7bd1474aaa8/orjson-3.10.18-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:69c34b9441b863175cc6a01f2935de994025e773f814412030f269da4f7be147", size = 249087 }, { url = "https://files.pythonhosted.org/packages/c2/f3/0dd6b4750eb556ae4e2c6a9cb3e219ec642e9c6d95f8ebe5dc9020c67204/orjson-3.11.2-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a079fdba7062ab396380eeedb589afb81dc6683f07f528a03b6f7aae420a0219", size = 226419 },
{ url = "https://files.pythonhosted.org/packages/bc/f7/7118f965541aeac6844fcb18d6988e111ac0d349c9b80cda53583e758908/orjson-3.10.18-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:1ebeda919725f9dbdb269f59bc94f861afbe2a27dce5608cdba2d92772364d1c", size = 133273 }, { url = "https://files.pythonhosted.org/packages/44/d5/e67f36277f78f2af8a4690e0c54da6b34169812f807fd1b4bfc4dbcf9558/orjson-3.11.2-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:6a5f62ebbc530bb8bb4b1ead103647b395ba523559149b91a6c545f7cd4110ad", size = 115803 },
{ url = "https://files.pythonhosted.org/packages/fb/d9/839637cc06eaf528dd8127b36004247bf56e064501f68df9ee6fd56a88ee/orjson-3.10.18-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5adf5f4eed520a4959d29ea80192fa626ab9a20b2ea13f8f6dc58644f6927103", size = 136779 }, { url = "https://files.pythonhosted.org/packages/24/37/ff8bc86e0dacc48f07c2b6e20852f230bf4435611bab65e3feae2b61f0ae/orjson-3.11.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7df6c7b8b0931feb3420b72838c3e2ba98c228f7aa60d461bc050cf4ca5f7b2", size = 111337 },
{ url = "https://files.pythonhosted.org/packages/2b/6d/f226ecfef31a1f0e7d6bf9a31a0bbaf384c7cbe3fce49cc9c2acc51f902a/orjson-3.10.18-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7592bb48a214e18cd670974f289520f12b7aed1fa0b2e2616b8ed9e069e08595", size = 132811 }, { url = "https://files.pythonhosted.org/packages/b9/25/37d4d3e8079ea9784ea1625029988e7f4594ce50d4738b0c1e2bf4a9e201/orjson-3.11.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6f59dfea7da1fced6e782bb3699718088b1036cb361f36c6e4dd843c5111aefe", size = 116222 },
{ url = "https://files.pythonhosted.org/packages/73/2d/371513d04143c85b681cf8f3bce743656eb5b640cb1f461dad750ac4b4d4/orjson-3.10.18-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f872bef9f042734110642b7a11937440797ace8c87527de25e0c53558b579ccc", size = 137018 }, { url = "https://files.pythonhosted.org/packages/b7/32/a63fd9c07fce3b4193dcc1afced5dd4b0f3a24e27556604e9482b32189c9/orjson-3.11.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edf49146520fef308c31aa4c45b9925fd9c7584645caca7c0c4217d7900214ae", size = 119020 },
{ url = "https://files.pythonhosted.org/packages/69/cb/a4d37a30507b7a59bdc484e4a3253c8141bf756d4e13fcc1da760a0b00cb/orjson-3.10.18-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0315317601149c244cb3ecef246ef5861a64824ccbcb8018d32c66a60a84ffbc", size = 138368 }, { url = "https://files.pythonhosted.org/packages/b4/b6/400792b8adc3079a6b5d649264a3224d6342436d9fac9a0ed4abc9dc4596/orjson-3.11.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50995bbeb5d41a32ad15e023305807f561ac5dcd9bd41a12c8d8d1d2c83e44e6", size = 120721 },
{ url = "https://files.pythonhosted.org/packages/1e/ae/cd10883c48d912d216d541eb3db8b2433415fde67f620afe6f311f5cd2ca/orjson-3.10.18-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0da26957e77e9e55a6c2ce2e7182a36a6f6b180ab7189315cb0995ec362e049", size = 142840 }, { url = "https://files.pythonhosted.org/packages/40/f3/31ab8f8c699eb9e65af8907889a0b7fef74c1d2b23832719a35da7bb0c58/orjson-3.11.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2cc42960515076eb639b705f105712b658c525863d89a1704d984b929b0577d1", size = 123574 },
{ url = "https://files.pythonhosted.org/packages/6d/4c/2bda09855c6b5f2c055034c9eda1529967b042ff8d81a05005115c4e6772/orjson-3.10.18-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb70d489bc79b7519e5803e2cc4c72343c9dc1154258adf2f8925d0b60da7c58", size = 133135 }, { url = "https://files.pythonhosted.org/packages/bd/a6/ce4287c412dff81878f38d06d2c80845709c60012ca8daf861cb064b4574/orjson-3.11.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c56777cab2a7b2a8ea687fedafb84b3d7fdafae382165c31a2adf88634c432fa", size = 121225 },
{ url = "https://files.pythonhosted.org/packages/13/4a/35971fd809a8896731930a80dfff0b8ff48eeb5d8b57bb4d0d525160017f/orjson-3.10.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9e86a6af31b92299b00736c89caf63816f70a4001e750bda179e15564d7a034", size = 134810 }, { url = "https://files.pythonhosted.org/packages/69/b0/7a881b2aef4fed0287d2a4fbb029d01ed84fa52b4a68da82bdee5e50598e/orjson-3.11.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:07349e88025b9b5c783077bf7a9f401ffbfb07fd20e86ec6fc5b7432c28c2c5e", size = 119201 },
{ url = "https://files.pythonhosted.org/packages/99/70/0fa9e6310cda98365629182486ff37a1c6578e34c33992df271a476ea1cd/orjson-3.10.18-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:c382a5c0b5931a5fc5405053d36c1ce3fd561694738626c77ae0b1dfc0242ca1", size = 413491 }, { url = "https://files.pythonhosted.org/packages/cf/98/a325726b37f7512ed6338e5e65035c3c6505f4e628b09a5daf0419f054ea/orjson-3.11.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:45841fbb79c96441a8c58aa29ffef570c5df9af91f0f7a9572e5505e12412f15", size = 392193 },
{ url = "https://files.pythonhosted.org/packages/32/cb/990a0e88498babddb74fb97855ae4fbd22a82960e9b06eab5775cac435da/orjson-3.10.18-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8e4b2ae732431127171b875cb2668f883e1234711d3c147ffd69fe5be51a8012", size = 153277 }, { url = "https://files.pythonhosted.org/packages/cb/4f/a7194f98b0ce1d28190e0c4caa6d091a3fc8d0107ad2209f75c8ba398984/orjson-3.11.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:13d8d8db6cd8d89d4d4e0f4161acbbb373a4d2a4929e862d1d2119de4aa324ac", size = 134548 },
{ url = "https://files.pythonhosted.org/packages/92/44/473248c3305bf782a384ed50dd8bc2d3cde1543d107138fd99b707480ca1/orjson-3.10.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d808e34ddb24fc29a4d4041dcfafbae13e129c93509b847b14432717d94b44f", size = 137367 }, { url = "https://files.pythonhosted.org/packages/e8/5e/b84caa2986c3f472dc56343ddb0167797a708a8d5c3be043e1e2677b55df/orjson-3.11.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51da1ee2178ed09c00d09c1b953e45846bbc16b6420965eb7a913ba209f606d8", size = 123798 },
{ url = "https://files.pythonhosted.org/packages/ad/fd/7f1d3edd4ffcd944a6a40e9f88af2197b619c931ac4d3cfba4798d4d3815/orjson-3.10.18-cp313-cp313-win32.whl", hash = "sha256:ad8eacbb5d904d5591f27dee4031e2c1db43d559edb8f91778efd642d70e6bea", size = 142687 }, { url = "https://files.pythonhosted.org/packages/9c/5b/e398449080ce6b4c8fcadad57e51fa16f65768e1b142ba90b23ac5d10801/orjson-3.11.2-cp313-cp313-win32.whl", hash = "sha256:51dc033df2e4a4c91c0ba4f43247de99b3cbf42ee7a42ee2b2b2f76c8b2f2cb5", size = 124402 },
{ url = "https://files.pythonhosted.org/packages/4b/03/c75c6ad46be41c16f4cfe0352a2d1450546f3c09ad2c9d341110cd87b025/orjson-3.10.18-cp313-cp313-win_amd64.whl", hash = "sha256:aed411bcb68bf62e85588f2a7e03a6082cc42e5a2796e06e72a962d7c6310b52", size = 134794 }, { url = "https://files.pythonhosted.org/packages/b3/66/429e4608e124debfc4790bfc37131f6958e59510ba3b542d5fc163be8e5f/orjson-3.11.2-cp313-cp313-win_amd64.whl", hash = "sha256:29d91d74942b7436f29b5d1ed9bcfc3f6ef2d4f7c4997616509004679936650d", size = 119498 },
{ url = "https://files.pythonhosted.org/packages/c2/28/f53038a5a72cc4fd0b56c1eafb4ef64aec9685460d5ac34de98ca78b6e29/orjson-3.10.18-cp313-cp313-win_arm64.whl", hash = "sha256:f54c1385a0e6aba2f15a40d703b858bedad36ded0491e55d35d905b2c34a4cc3", size = 131186 }, { url = "https://files.pythonhosted.org/packages/7b/04/f8b5f317cce7ad3580a9ad12d7e2df0714dfa8a83328ecddd367af802f5b/orjson-3.11.2-cp313-cp313-win_arm64.whl", hash = "sha256:4ca4fb5ac21cd1e48028d4f708b1bb13e39c42d45614befd2ead004a8bba8535", size = 114051 },
{ url = "https://files.pythonhosted.org/packages/74/83/2c363022b26c3c25b3708051a19d12f3374739bb81323f05b284392080c0/orjson-3.11.2-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:3dcba7101ea6a8d4ef060746c0f2e7aa8e2453a1012083e1ecce9726d7554cb7", size = 226406 },
{ url = "https://files.pythonhosted.org/packages/b0/a7/aa3c973de0b33fc93b4bd71691665ffdfeae589ea9d0625584ab10a7d0f5/orjson-3.11.2-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:15d17bdb76a142e1f55d91913e012e6e6769659daa6bfef3ef93f11083137e81", size = 115788 },
{ url = "https://files.pythonhosted.org/packages/ef/f2/e45f233dfd09fdbb052ec46352363dca3906618e1a2b264959c18f809d0b/orjson-3.11.2-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:53c9e81768c69d4b66b8876ec3c8e431c6e13477186d0db1089d82622bccd19f", size = 111318 },
{ url = "https://files.pythonhosted.org/packages/3e/23/cf5a73c4da6987204cbbf93167f353ff0c5013f7c5e5ef845d4663a366da/orjson-3.11.2-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:d4f13af59a7b84c1ca6b8a7ab70d608f61f7c44f9740cd42409e6ae7b6c8d8b7", size = 121231 },
{ url = "https://files.pythonhosted.org/packages/40/1d/47468a398ae68a60cc21e599144e786e035bb12829cb587299ecebc088f1/orjson-3.11.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bde64aa469b5ee46cc960ed241fae3721d6a8801dacb2ca3466547a2535951e4", size = 119204 },
{ url = "https://files.pythonhosted.org/packages/4d/d9/f99433d89b288b5bc8836bffb32a643f805e673cf840ef8bab6e73ced0d1/orjson-3.11.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:b5ca86300aeb383c8fa759566aca065878d3d98c3389d769b43f0a2e84d52c5f", size = 392237 },
{ url = "https://files.pythonhosted.org/packages/d4/dc/1b9d80d40cebef603325623405136a29fb7d08c877a728c0943dd066c29a/orjson-3.11.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:24e32a558ebed73a6a71c8f1cbc163a7dd5132da5270ff3d8eeb727f4b6d1bc7", size = 134578 },
{ url = "https://files.pythonhosted.org/packages/45/b3/72e7a4c5b6485ef4e83ef6aba7f1dd041002bad3eb5d1d106ca5b0fc02c6/orjson-3.11.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e36319a5d15b97e4344110517450396845cc6789aed712b1fbf83c1bd95792f6", size = 123799 },
{ url = "https://files.pythonhosted.org/packages/c8/3e/a3d76b392e7acf9b34dc277171aad85efd6accc75089bb35b4c614990ea9/orjson-3.11.2-cp314-cp314-win32.whl", hash = "sha256:40193ada63fab25e35703454d65b6afc71dbc65f20041cb46c6d91709141ef7f", size = 124461 },
{ url = "https://files.pythonhosted.org/packages/fb/e3/75c6a596ff8df9e4a5894813ff56695f0a218e6ea99420b4a645c4f7795d/orjson-3.11.2-cp314-cp314-win_amd64.whl", hash = "sha256:7c8ac5f6b682d3494217085cf04dadae66efee45349ad4ee2a1da3c97e2305a8", size = 119494 },
{ url = "https://files.pythonhosted.org/packages/5b/3d/9e74742fc261c5ca473c96bb3344d03995869e1dc6402772c60afb97736a/orjson-3.11.2-cp314-cp314-win_arm64.whl", hash = "sha256:21cf261e8e79284242e4cb1e5924df16ae28255184aafeff19be1405f6d33f67", size = 114046 },
] ]
[[package]] [[package]]
@@ -379,79 +390,104 @@ wheels = [
[[package]] [[package]]
name = "pillow" name = "pillow"
version = "11.2.1" version = "11.3.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/af/cb/bb5c01fcd2a69335b86c22142b2bccfc3464087efb7fd382eee5ffc7fdf7/pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6", size = 47026707 } sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/0d/8b/b158ad57ed44d3cc54db8d68ad7c0a58b8fc0e4c7a3f995f9d62d5b464a1/pillow-11.2.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:d57a75d53922fc20c165016a20d9c44f73305e67c351bbc60d1adaf662e74047", size = 3198442 }, { url = "https://files.pythonhosted.org/packages/4c/5d/45a3553a253ac8763f3561371432a90bdbe6000fbdcf1397ffe502aa206c/pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860", size = 5316554 },
{ url = "https://files.pythonhosted.org/packages/b1/f8/bb5d956142f86c2d6cc36704943fa761f2d2e4c48b7436fd0a85c20f1713/pillow-11.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:127bf6ac4a5b58b3d32fc8289656f77f80567d65660bc46f72c0d77e6600cc95", size = 3030553 }, { url = "https://files.pythonhosted.org/packages/7c/c8/67c12ab069ef586a25a4a79ced553586748fad100c77c0ce59bb4983ac98/pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad", size = 4686548 },
{ url = "https://files.pythonhosted.org/packages/22/7f/0e413bb3e2aa797b9ca2c5c38cb2e2e45d88654e5b12da91ad446964cfae/pillow-11.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4ba4be812c7a40280629e55ae0b14a0aafa150dd6451297562e1764808bbe61", size = 4405503 }, { url = "https://files.pythonhosted.org/packages/2f/bd/6741ebd56263390b382ae4c5de02979af7f8bd9807346d068700dd6d5cf9/pillow-11.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0", size = 5859742 },
{ url = "https://files.pythonhosted.org/packages/f3/b4/cc647f4d13f3eb837d3065824aa58b9bcf10821f029dc79955ee43f793bd/pillow-11.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8bd62331e5032bc396a93609982a9ab6b411c05078a52f5fe3cc59234a3abd1", size = 4490648 }, { url = "https://files.pythonhosted.org/packages/ca/0b/c412a9e27e1e6a829e6ab6c2dca52dd563efbedf4c9c6aa453d9a9b77359/pillow-11.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b", size = 7633087 },
{ url = "https://files.pythonhosted.org/packages/c2/6f/240b772a3b35cdd7384166461567aa6713799b4e78d180c555bd284844ea/pillow-11.2.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:562d11134c97a62fe3af29581f083033179f7ff435f78392565a1ad2d1c2c45c", size = 4508937 }, { url = "https://files.pythonhosted.org/packages/59/9d/9b7076aaf30f5dd17e5e5589b2d2f5a5d7e30ff67a171eb686e4eecc2adf/pillow-11.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50", size = 5963350 },
{ url = "https://files.pythonhosted.org/packages/f3/5e/7ca9c815ade5fdca18853db86d812f2f188212792780208bdb37a0a6aef4/pillow-11.2.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c97209e85b5be259994eb5b69ff50c5d20cca0f458ef9abd835e262d9d88b39d", size = 4599802 }, { url = "https://files.pythonhosted.org/packages/f0/16/1a6bf01fb622fb9cf5c91683823f073f053005c849b1f52ed613afcf8dae/pillow-11.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae", size = 6631840 },
{ url = "https://files.pythonhosted.org/packages/02/81/c3d9d38ce0c4878a77245d4cf2c46d45a4ad0f93000227910a46caff52f3/pillow-11.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0c3e6d0f59171dfa2e25d7116217543310908dfa2770aa64b8f87605f8cacc97", size = 4576717 }, { url = "https://files.pythonhosted.org/packages/7b/e6/6ff7077077eb47fde78739e7d570bdcd7c10495666b6afcd23ab56b19a43/pillow-11.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9", size = 6074005 },
{ url = "https://files.pythonhosted.org/packages/42/49/52b719b89ac7da3185b8d29c94d0e6aec8140059e3d8adcaa46da3751180/pillow-11.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc1c3bc53befb6096b84165956e886b1729634a799e9d6329a0c512ab651e579", size = 4654874 }, { url = "https://files.pythonhosted.org/packages/c3/3a/b13f36832ea6d279a697231658199e0a03cd87ef12048016bdcc84131601/pillow-11.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040a5b691b0713e1f6cbe222e0f4f74cd233421e105850ae3b3c0ceda520f42e", size = 6708372 },
{ url = "https://files.pythonhosted.org/packages/5b/0b/ede75063ba6023798267023dc0d0401f13695d228194d2242d5a7ba2f964/pillow-11.2.1-cp310-cp310-win32.whl", hash = "sha256:312c77b7f07ab2139924d2639860e084ec2a13e72af54d4f08ac843a5fc9c79d", size = 2331717 }, { url = "https://files.pythonhosted.org/packages/6c/e4/61b2e1a7528740efbc70b3d581f33937e38e98ef3d50b05007267a55bcb2/pillow-11.3.0-cp310-cp310-win32.whl", hash = "sha256:89bd777bc6624fe4115e9fac3352c79ed60f3bb18651420635f26e643e3dd1f6", size = 6277090 },
{ url = "https://files.pythonhosted.org/packages/ed/3c/9831da3edea527c2ed9a09f31a2c04e77cd705847f13b69ca60269eec370/pillow-11.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:9bc7ae48b8057a611e5fe9f853baa88093b9a76303937449397899385da06fad", size = 2676204 }, { url = "https://files.pythonhosted.org/packages/a9/d3/60c781c83a785d6afbd6a326ed4d759d141de43aa7365725cbcd65ce5e54/pillow-11.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:19d2ff547c75b8e3ff46f4d9ef969a06c30ab2d4263a9e287733aa8b2429ce8f", size = 6985988 },
{ url = "https://files.pythonhosted.org/packages/01/97/1f66ff8a1503d8cbfc5bae4dc99d54c6ec1e22ad2b946241365320caabc2/pillow-11.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:2728567e249cdd939f6cc3d1f049595c66e4187f3c34078cbc0a7d21c47482d2", size = 2414767 }, { url = "https://files.pythonhosted.org/packages/9f/28/4f4a0203165eefb3763939c6789ba31013a2e90adffb456610f30f613850/pillow-11.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f", size = 2422899 },
{ url = "https://files.pythonhosted.org/packages/68/08/3fbf4b98924c73037a8e8b4c2c774784805e0fb4ebca6c5bb60795c40125/pillow-11.2.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35ca289f712ccfc699508c4658a1d14652e8033e9b69839edf83cbdd0ba39e70", size = 3198450 }, { url = "https://files.pythonhosted.org/packages/db/26/77f8ed17ca4ffd60e1dcd220a6ec6d71210ba398cfa33a13a1cd614c5613/pillow-11.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722", size = 5316531 },
{ url = "https://files.pythonhosted.org/packages/84/92/6505b1af3d2849d5e714fc75ba9e69b7255c05ee42383a35a4d58f576b16/pillow-11.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0409af9f829f87a2dfb7e259f78f317a5351f2045158be321fd135973fff7bf", size = 3030550 }, { url = "https://files.pythonhosted.org/packages/cb/39/ee475903197ce709322a17a866892efb560f57900d9af2e55f86db51b0a5/pillow-11.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288", size = 4686560 },
{ url = "https://files.pythonhosted.org/packages/3c/8c/ac2f99d2a70ff966bc7eb13dacacfaab57c0549b2ffb351b6537c7840b12/pillow-11.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e5c5edee874dce4f653dbe59db7c73a600119fbea8d31f53423586ee2aafd7", size = 4415018 }, { url = "https://files.pythonhosted.org/packages/d5/90/442068a160fd179938ba55ec8c97050a612426fae5ec0a764e345839f76d/pillow-11.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d", size = 5870978 },
{ url = "https://files.pythonhosted.org/packages/1f/e3/0a58b5d838687f40891fff9cbaf8669f90c96b64dc8f91f87894413856c6/pillow-11.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b93a07e76d13bff9444f1a029e0af2964e654bfc2e2c2d46bfd080df5ad5f3d8", size = 4498006 }, { url = "https://files.pythonhosted.org/packages/13/92/dcdd147ab02daf405387f0218dcf792dc6dd5b14d2573d40b4caeef01059/pillow-11.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494", size = 7641168 },
{ url = "https://files.pythonhosted.org/packages/21/f5/6ba14718135f08fbfa33308efe027dd02b781d3f1d5c471444a395933aac/pillow-11.2.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:e6def7eed9e7fa90fde255afaf08060dc4b343bbe524a8f69bdd2a2f0018f600", size = 4517773 }, { url = "https://files.pythonhosted.org/packages/6e/db/839d6ba7fd38b51af641aa904e2960e7a5644d60ec754c046b7d2aee00e5/pillow-11.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58", size = 5973053 },
{ url = "https://files.pythonhosted.org/packages/20/f2/805ad600fc59ebe4f1ba6129cd3a75fb0da126975c8579b8f57abeb61e80/pillow-11.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8f4f3724c068be008c08257207210c138d5f3731af6c155a81c2b09a9eb3a788", size = 4607069 }, { url = "https://files.pythonhosted.org/packages/f2/2f/d7675ecae6c43e9f12aa8d58b6012683b20b6edfbdac7abcb4e6af7a3784/pillow-11.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f", size = 6640273 },
{ url = "https://files.pythonhosted.org/packages/71/6b/4ef8a288b4bb2e0180cba13ca0a519fa27aa982875882392b65131401099/pillow-11.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a0a6709b47019dff32e678bc12c63008311b82b9327613f534e496dacaefb71e", size = 4583460 }, { url = "https://files.pythonhosted.org/packages/45/ad/931694675ede172e15b2ff03c8144a0ddaea1d87adb72bb07655eaffb654/pillow-11.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e", size = 6082043 },
{ url = "https://files.pythonhosted.org/packages/62/ae/f29c705a09cbc9e2a456590816e5c234382ae5d32584f451c3eb41a62062/pillow-11.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f6b0c664ccb879109ee3ca702a9272d877f4fcd21e5eb63c26422fd6e415365e", size = 4661304 }, { url = "https://files.pythonhosted.org/packages/3a/04/ba8f2b11fc80d2dd462d7abec16351b45ec99cbbaea4387648a44190351a/pillow-11.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:932c754c2d51ad2b2271fd01c3d121daaa35e27efae2a616f77bf164bc0b3e94", size = 6715516 },
{ url = "https://files.pythonhosted.org/packages/6e/1a/c8217b6f2f73794a5e219fbad087701f412337ae6dbb956db37d69a9bc43/pillow-11.2.1-cp311-cp311-win32.whl", hash = "sha256:cc5d875d56e49f112b6def6813c4e3d3036d269c008bf8aef72cd08d20ca6df6", size = 2331809 }, { url = "https://files.pythonhosted.org/packages/48/59/8cd06d7f3944cc7d892e8533c56b0acb68399f640786313275faec1e3b6f/pillow-11.3.0-cp311-cp311-win32.whl", hash = "sha256:b4b8f3efc8d530a1544e5962bd6b403d5f7fe8b9e08227c6b255f98ad82b4ba0", size = 6274768 },
{ url = "https://files.pythonhosted.org/packages/e2/72/25a8f40170dc262e86e90f37cb72cb3de5e307f75bf4b02535a61afcd519/pillow-11.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:0f5c7eda47bf8e3c8a283762cab94e496ba977a420868cb819159980b6709193", size = 2676338 }, { url = "https://files.pythonhosted.org/packages/f1/cc/29c0f5d64ab8eae20f3232da8f8571660aa0ab4b8f1331da5c2f5f9a938e/pillow-11.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:1a992e86b0dd7aeb1f053cd506508c0999d710a8f07b4c791c63843fc6a807ac", size = 6986055 },
{ url = "https://files.pythonhosted.org/packages/06/9e/76825e39efee61efea258b479391ca77d64dbd9e5804e4ad0fa453b4ba55/pillow-11.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:4d375eb838755f2528ac8cbc926c3e31cc49ca4ad0cf79cff48b20e30634a4a7", size = 2414918 }, { url = "https://files.pythonhosted.org/packages/c6/df/90bd886fabd544c25addd63e5ca6932c86f2b701d5da6c7839387a076b4a/pillow-11.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd", size = 2423079 },
{ url = "https://files.pythonhosted.org/packages/c7/40/052610b15a1b8961f52537cc8326ca6a881408bc2bdad0d852edeb6ed33b/pillow-11.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:78afba22027b4accef10dbd5eed84425930ba41b3ea0a86fa8d20baaf19d807f", size = 3190185 }, { url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", size = 5278800 },
{ url = "https://files.pythonhosted.org/packages/e5/7e/b86dbd35a5f938632093dc40d1682874c33dcfe832558fc80ca56bfcb774/pillow-11.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78092232a4ab376a35d68c4e6d5e00dfd73454bd12b230420025fbe178ee3b0b", size = 3030306 }, { url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", size = 4686296 },
{ url = "https://files.pythonhosted.org/packages/a4/5c/467a161f9ed53e5eab51a42923c33051bf8d1a2af4626ac04f5166e58e0c/pillow-11.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a5f306095c6780c52e6bbb6109624b95c5b18e40aab1c3041da3e9e0cd3e2d", size = 4416121 }, { url = "https://files.pythonhosted.org/packages/8e/1e/b9e12bbe6e4c2220effebc09ea0923a07a6da1e1f1bfbc8d7d29a01ce32b/pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d", size = 5871726 },
{ url = "https://files.pythonhosted.org/packages/62/73/972b7742e38ae0e2ac76ab137ca6005dcf877480da0d9d61d93b613065b4/pillow-11.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c7b29dbd4281923a2bfe562acb734cee96bbb129e96e6972d315ed9f232bef4", size = 4501707 }, { url = "https://files.pythonhosted.org/packages/8d/33/e9200d2bd7ba00dc3ddb78df1198a6e80d7669cce6c2bdbeb2530a74ec58/pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6", size = 7644652 },
{ url = "https://files.pythonhosted.org/packages/e4/3a/427e4cb0b9e177efbc1a84798ed20498c4f233abde003c06d2650a6d60cb/pillow-11.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3e645b020f3209a0181a418bffe7b4a93171eef6c4ef6cc20980b30bebf17b7d", size = 4522921 }, { url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", size = 5977787 },
{ url = "https://files.pythonhosted.org/packages/fe/7c/d8b1330458e4d2f3f45d9508796d7caf0c0d3764c00c823d10f6f1a3b76d/pillow-11.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2dbea1012ccb784a65349f57bbc93730b96e85b42e9bf7b01ef40443db720b4", size = 4612523 }, { url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", size = 6645236 },
{ url = "https://files.pythonhosted.org/packages/b3/2f/65738384e0b1acf451de5a573d8153fe84103772d139e1e0bdf1596be2ea/pillow-11.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:da3104c57bbd72948d75f6a9389e6727d2ab6333c3617f0a89d72d4940aa0443", size = 4587836 }, { url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", size = 6086950 },
{ url = "https://files.pythonhosted.org/packages/6a/c5/e795c9f2ddf3debb2dedd0df889f2fe4b053308bb59a3cc02a0cd144d641/pillow-11.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:598174aef4589af795f66f9caab87ba4ff860ce08cd5bb447c6fc553ffee603c", size = 4669390 }, { url = "https://files.pythonhosted.org/packages/0b/1a/7cff92e695a2a29ac1958c2a0fe4c0b2393b60aac13b04a4fe2735cad52d/pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d", size = 6723358 },
{ url = "https://files.pythonhosted.org/packages/96/ae/ca0099a3995976a9fce2f423166f7bff9b12244afdc7520f6ed38911539a/pillow-11.2.1-cp312-cp312-win32.whl", hash = "sha256:1d535df14716e7f8776b9e7fee118576d65572b4aad3ed639be9e4fa88a1cad3", size = 2332309 }, { url = "https://files.pythonhosted.org/packages/26/7d/73699ad77895f69edff76b0f332acc3d497f22f5d75e5360f78cbcaff248/pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149", size = 6275079 },
{ url = "https://files.pythonhosted.org/packages/7c/18/24bff2ad716257fc03da964c5e8f05d9790a779a8895d6566e493ccf0189/pillow-11.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:14e33b28bf17c7a38eede290f77db7c664e4eb01f7869e37fa98a5aa95978941", size = 2676768 }, { url = "https://files.pythonhosted.org/packages/8c/ce/e7dfc873bdd9828f3b6e5c2bbb74e47a98ec23cc5c74fc4e54462f0d9204/pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d", size = 6986324 },
{ url = "https://files.pythonhosted.org/packages/da/bb/e8d656c9543276517ee40184aaa39dcb41e683bca121022f9323ae11b39d/pillow-11.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:21e1470ac9e5739ff880c211fc3af01e3ae505859392bf65458c224d0bf283eb", size = 2415087 }, { url = "https://files.pythonhosted.org/packages/16/8f/b13447d1bf0b1f7467ce7d86f6e6edf66c0ad7cf44cf5c87a37f9bed9936/pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542", size = 2423067 },
{ url = "https://files.pythonhosted.org/packages/36/9c/447528ee3776e7ab8897fe33697a7ff3f0475bb490c5ac1456a03dc57956/pillow-11.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fdec757fea0b793056419bca3e9932eb2b0ceec90ef4813ea4c1e072c389eb28", size = 3190098 }, { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328 },
{ url = "https://files.pythonhosted.org/packages/b5/09/29d5cd052f7566a63e5b506fac9c60526e9ecc553825551333e1e18a4858/pillow-11.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0e130705d568e2f43a17bcbe74d90958e8a16263868a12c3e0d9c8162690830", size = 3030166 }, { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652 },
{ url = "https://files.pythonhosted.org/packages/71/5d/446ee132ad35e7600652133f9c2840b4799bbd8e4adba881284860da0a36/pillow-11.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bdb5e09068332578214cadd9c05e3d64d99e0e87591be22a324bdbc18925be0", size = 4408674 }, { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443 },
{ url = "https://files.pythonhosted.org/packages/69/5f/cbe509c0ddf91cc3a03bbacf40e5c2339c4912d16458fcb797bb47bcb269/pillow-11.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d189ba1bebfbc0c0e529159631ec72bb9e9bc041f01ec6d3233d6d82eb823bc1", size = 4496005 }, { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474 },
{ url = "https://files.pythonhosted.org/packages/f9/b3/dd4338d8fb8a5f312021f2977fb8198a1184893f9b00b02b75d565c33b51/pillow-11.2.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:191955c55d8a712fab8934a42bfefbf99dd0b5875078240943f913bb66d46d9f", size = 4518707 }, { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038 },
{ url = "https://files.pythonhosted.org/packages/13/eb/2552ecebc0b887f539111c2cd241f538b8ff5891b8903dfe672e997529be/pillow-11.2.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:ad275964d52e2243430472fc5d2c2334b4fc3ff9c16cb0a19254e25efa03a155", size = 4610008 }, { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407 },
{ url = "https://files.pythonhosted.org/packages/72/d1/924ce51bea494cb6e7959522d69d7b1c7e74f6821d84c63c3dc430cbbf3b/pillow-11.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:750f96efe0597382660d8b53e90dd1dd44568a8edb51cb7f9d5d918b80d4de14", size = 4585420 }, { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094 },
{ url = "https://files.pythonhosted.org/packages/43/ab/8f81312d255d713b99ca37479a4cb4b0f48195e530cdc1611990eb8fd04b/pillow-11.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fe15238d3798788d00716637b3d4e7bb6bde18b26e5d08335a96e88564a36b6b", size = 4667655 }, { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503 },
{ url = "https://files.pythonhosted.org/packages/94/86/8f2e9d2dc3d308dfd137a07fe1cc478df0a23d42a6c4093b087e738e4827/pillow-11.2.1-cp313-cp313-win32.whl", hash = "sha256:3fe735ced9a607fee4f481423a9c36701a39719252a9bb251679635f99d0f7d2", size = 2332329 }, { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574 },
{ url = "https://files.pythonhosted.org/packages/6d/ec/1179083b8d6067a613e4d595359b5fdea65d0a3b7ad623fee906e1b3c4d2/pillow-11.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:74ee3d7ecb3f3c05459ba95eed5efa28d6092d751ce9bf20e3e253a4e497e691", size = 2676388 }, { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060 },
{ url = "https://files.pythonhosted.org/packages/23/f1/2fc1e1e294de897df39fa8622d829b8828ddad938b0eaea256d65b84dd72/pillow-11.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:5119225c622403afb4b44bad4c1ca6c1f98eed79db8d3bc6e4e160fc6339d66c", size = 2414950 }, { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407 },
{ url = "https://files.pythonhosted.org/packages/c4/3e/c328c48b3f0ead7bab765a84b4977acb29f101d10e4ef57a5e3400447c03/pillow-11.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8ce2e8411c7aaef53e6bb29fe98f28cd4fbd9a1d9be2eeea434331aac0536b22", size = 3192759 }, { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841 },
{ url = "https://files.pythonhosted.org/packages/18/0e/1c68532d833fc8b9f404d3a642991441d9058eccd5606eab31617f29b6d4/pillow-11.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9ee66787e095127116d91dea2143db65c7bb1e232f617aa5957c0d9d2a3f23a7", size = 3033284 }, { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450 },
{ url = "https://files.pythonhosted.org/packages/b7/cb/6faf3fb1e7705fd2db74e070f3bf6f88693601b0ed8e81049a8266de4754/pillow-11.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9622e3b6c1d8b551b6e6f21873bdcc55762b4b2126633014cea1803368a9aa16", size = 4445826 }, { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055 },
{ url = "https://files.pythonhosted.org/packages/07/94/8be03d50b70ca47fb434a358919d6a8d6580f282bbb7af7e4aa40103461d/pillow-11.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63b5dff3a68f371ea06025a1a6966c9a1e1ee452fc8020c2cd0ea41b83e9037b", size = 4527329 }, { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110 },
{ url = "https://files.pythonhosted.org/packages/fd/a4/bfe78777076dc405e3bd2080bc32da5ab3945b5a25dc5d8acaa9de64a162/pillow-11.2.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:31df6e2d3d8fc99f993fd253e97fae451a8db2e7207acf97859732273e108406", size = 4549049 }, { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547 },
{ url = "https://files.pythonhosted.org/packages/65/4d/eaf9068dc687c24979e977ce5677e253624bd8b616b286f543f0c1b91662/pillow-11.2.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:062b7a42d672c45a70fa1f8b43d1d38ff76b63421cbbe7f88146b39e8a558d91", size = 4635408 }, { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554 },
{ url = "https://files.pythonhosted.org/packages/1d/26/0fd443365d9c63bc79feb219f97d935cd4b93af28353cba78d8e77b61719/pillow-11.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4eb92eca2711ef8be42fd3f67533765d9fd043b8c80db204f16c8ea62ee1a751", size = 4614863 }, { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132 },
{ url = "https://files.pythonhosted.org/packages/49/65/dca4d2506be482c2c6641cacdba5c602bc76d8ceb618fd37de855653a419/pillow-11.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f91ebf30830a48c825590aede79376cb40f110b387c17ee9bd59932c961044f9", size = 4692938 }, { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001 },
{ url = "https://files.pythonhosted.org/packages/b3/92/1ca0c3f09233bd7decf8f7105a1c4e3162fb9142128c74adad0fb361b7eb/pillow-11.2.1-cp313-cp313t-win32.whl", hash = "sha256:e0b55f27f584ed623221cfe995c912c61606be8513bfa0e07d2c674b4516d9dd", size = 2335774 }, { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814 },
{ url = "https://files.pythonhosted.org/packages/a5/ac/77525347cb43b83ae905ffe257bbe2cc6fd23acb9796639a1f56aa59d191/pillow-11.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:36d6b82164c39ce5482f649b437382c0fb2395eabc1e2b1702a6deb8ad647d6e", size = 2681895 }, { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124 },
{ url = "https://files.pythonhosted.org/packages/67/32/32dc030cfa91ca0fc52baebbba2e009bb001122a1daa8b6a79ad830b38d3/pillow-11.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:225c832a13326e34f212d2072982bb1adb210e0cc0b153e688743018c94a2681", size = 2417234 }, { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186 },
{ url = "https://files.pythonhosted.org/packages/33/49/c8c21e4255b4f4a2c0c68ac18125d7f5460b109acc6dfdef1a24f9b960ef/pillow-11.2.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:9b7b0d4fd2635f54ad82785d56bc0d94f147096493a79985d0ab57aedd563156", size = 3181727 }, { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546 },
{ url = "https://files.pythonhosted.org/packages/6d/f1/f7255c0838f8c1ef6d55b625cfb286835c17e8136ce4351c5577d02c443b/pillow-11.2.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:aa442755e31c64037aa7c1cb186e0b369f8416c567381852c63444dd666fb772", size = 2999833 }, { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102 },
{ url = "https://files.pythonhosted.org/packages/e2/57/9968114457bd131063da98d87790d080366218f64fa2943b65ac6739abb3/pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0d3348c95b766f54b76116d53d4cb171b52992a1027e7ca50c81b43b9d9e363", size = 3437472 }, { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803 },
{ url = "https://files.pythonhosted.org/packages/b2/1b/e35d8a158e21372ecc48aac9c453518cfe23907bb82f950d6e1c72811eb0/pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85d27ea4c889342f7e35f6d56e7e1cb345632ad592e8c51b693d7b7556043ce0", size = 3459976 }, { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520 },
{ url = "https://files.pythonhosted.org/packages/26/da/2c11d03b765efff0ccc473f1c4186dc2770110464f2177efaed9cf6fae01/pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bf2c33d6791c598142f00c9c4c7d47f6476731c31081331664eb26d6ab583e01", size = 3527133 }, { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116 },
{ url = "https://files.pythonhosted.org/packages/79/1a/4e85bd7cadf78412c2a3069249a09c32ef3323650fd3005c97cca7aa21df/pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e616e7154c37669fc1dfc14584f11e284e05d1c650e1c0f972f281c4ccc53193", size = 3571555 }, { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597 },
{ url = "https://files.pythonhosted.org/packages/69/03/239939915216de1e95e0ce2334bf17a7870ae185eb390fab6d706aadbfc0/pillow-11.2.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:39ad2e0f424394e3aebc40168845fee52df1394a4673a6ee512d840d14ab3013", size = 2674713 }, { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246 },
{ url = "https://files.pythonhosted.org/packages/a4/ad/2613c04633c7257d9481ab21d6b5364b59fc5d75faafd7cb8693523945a3/pillow-11.2.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:80f1df8dbe9572b4b7abdfa17eb5d78dd620b1d55d9e25f834efdbee872d3aed", size = 3181734 }, { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336 },
{ url = "https://files.pythonhosted.org/packages/a4/fd/dcdda4471ed667de57bb5405bb42d751e6cfdd4011a12c248b455c778e03/pillow-11.2.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ea926cfbc3957090becbcbbb65ad177161a2ff2ad578b5a6ec9bb1e1cd78753c", size = 2999841 }, { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699 },
{ url = "https://files.pythonhosted.org/packages/ac/89/8a2536e95e77432833f0db6fd72a8d310c8e4272a04461fb833eb021bf94/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:738db0e0941ca0376804d4de6a782c005245264edaa253ffce24e5a15cbdc7bd", size = 3437470 }, { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789 },
{ url = "https://files.pythonhosted.org/packages/9d/8f/abd47b73c60712f88e9eda32baced7bfc3e9bd6a7619bb64b93acff28c3e/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db98ab6565c69082ec9b0d4e40dd9f6181dab0dd236d26f7a50b8b9bfbd5076", size = 3460013 }, { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386 },
{ url = "https://files.pythonhosted.org/packages/f6/20/5c0a0aa83b213b7a07ec01e71a3d6ea2cf4ad1d2c686cc0168173b6089e7/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:036e53f4170e270ddb8797d4c590e6dd14d28e15c7da375c18978045f7e6c37b", size = 3527165 }, { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911 },
{ url = "https://files.pythonhosted.org/packages/58/0e/2abab98a72202d91146abc839e10c14f7cf36166f12838ea0c4db3ca6ecb/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:14f73f7c291279bd65fda51ee87affd7c1e097709f7fdd0188957a16c264601f", size = 3571586 }, { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383 },
{ url = "https://files.pythonhosted.org/packages/21/2c/5e05f58658cf49b6667762cca03d6e7d85cededde2caf2ab37b81f80e574/pillow-11.2.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:208653868d5c9ecc2b327f9b9ef34e0e42a4cdd172c2988fd81d62d2bc9bc044", size = 2674751 }, { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385 },
{ url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129 },
{ url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580 },
{ url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860 },
{ url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694 },
{ url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888 },
{ url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330 },
{ url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089 },
{ url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206 },
{ url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370 },
{ url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500 },
{ url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835 },
{ url = "https://files.pythonhosted.org/packages/6f/8b/209bd6b62ce8367f47e68a218bffac88888fdf2c9fcf1ecadc6c3ec1ebc7/pillow-11.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967", size = 5270556 },
{ url = "https://files.pythonhosted.org/packages/2e/e6/231a0b76070c2cfd9e260a7a5b504fb72da0a95279410fa7afd99d9751d6/pillow-11.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe", size = 4654625 },
{ url = "https://files.pythonhosted.org/packages/13/f4/10cf94fda33cb12765f2397fc285fa6d8eb9c29de7f3185165b702fc7386/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c", size = 4874207 },
{ url = "https://files.pythonhosted.org/packages/72/c9/583821097dc691880c92892e8e2d41fe0a5a3d6021f4963371d2f6d57250/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25", size = 6583939 },
{ url = "https://files.pythonhosted.org/packages/3b/8e/5c9d410f9217b12320efc7c413e72693f48468979a013ad17fd690397b9a/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27", size = 4957166 },
{ url = "https://files.pythonhosted.org/packages/62/bb/78347dbe13219991877ffb3a91bf09da8317fbfcd4b5f9140aeae020ad71/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a", size = 5581482 },
{ url = "https://files.pythonhosted.org/packages/d9/28/1000353d5e61498aaeaaf7f1e4b49ddb05f2c6575f9d4f9f914a3538b6e1/pillow-11.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f", size = 6984596 },
{ url = "https://files.pythonhosted.org/packages/9e/e3/6fa84033758276fb31da12e5fb66ad747ae83b93c67af17f8c6ff4cc8f34/pillow-11.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6", size = 5270566 },
{ url = "https://files.pythonhosted.org/packages/5b/ee/e8d2e1ab4892970b561e1ba96cbd59c0d28cf66737fc44abb2aec3795a4e/pillow-11.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438", size = 4654618 },
{ url = "https://files.pythonhosted.org/packages/f2/6d/17f80f4e1f0761f02160fc433abd4109fa1548dcfdca46cfdadaf9efa565/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3", size = 4874248 },
{ url = "https://files.pythonhosted.org/packages/de/5f/c22340acd61cef960130585bbe2120e2fd8434c214802f07e8c03596b17e/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c", size = 6583963 },
{ url = "https://files.pythonhosted.org/packages/31/5e/03966aedfbfcbb4d5f8aa042452d3361f325b963ebbadddac05b122e47dd/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361", size = 4957170 },
{ url = "https://files.pythonhosted.org/packages/cc/2d/e082982aacc927fc2cab48e1e731bdb1643a1406acace8bed0900a61464e/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7", size = 5581505 },
{ url = "https://files.pythonhosted.org/packages/34/e7/ae39f538fd6844e982063c3a5e4598b8ced43b9633baa3a85ef33af8c05c/pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8", size = 6984598 },
] ]
[[package]] [[package]]
@@ -496,15 +532,15 @@ dev = [
requires-dist = [ requires-dist = [
{ name = "babel", specifier = ">=2.17.0" }, { name = "babel", specifier = ">=2.17.0" },
{ name = "beautifulsoup4", specifier = ">=4.13.4" }, { name = "beautifulsoup4", specifier = ">=4.13.4" },
{ name = "evdev", specifier = ">=1.9.1" }, { name = "evdev", specifier = ">=1.9.2" },
{ name = "icoextract", specifier = ">=0.1.6" }, { name = "icoextract", specifier = ">=0.2.0" },
{ name = "numpy", specifier = ">=2.2.4" }, { name = "numpy", specifier = ">=2.2.4" },
{ name = "orjson", specifier = ">=3.10.16" }, { name = "orjson", specifier = ">=3.11.2" },
{ name = "pillow", specifier = ">=11.2.1" }, { name = "pillow", specifier = ">=11.3.0" },
{ name = "psutil", specifier = ">=7.0.0" }, { name = "psutil", specifier = ">=7.0.0" },
{ name = "pyside6", specifier = ">=6.9.0" }, { name = "pyside6", specifier = ">=6.9.1" },
{ name = "pyudev", specifier = ">=0.24.3" }, { name = "pyudev", specifier = ">=0.24.3" },
{ name = "requests", specifier = ">=2.32.3" }, { name = "requests", specifier = ">=2.32.4" },
{ name = "tqdm", specifier = ">=4.67.1" }, { name = "tqdm", specifier = ">=4.67.1" },
{ name = "vdf", specifier = ">=3.4" }, { name = "vdf", specifier = ">=3.4" },
{ name = "websocket-client", specifier = ">=1.8.0" }, { name = "websocket-client", specifier = ">=1.8.0" },
@@ -512,14 +548,14 @@ requires-dist = [
[package.metadata.requires-dev] [package.metadata.requires-dev]
dev = [ dev = [
{ name = "pre-commit", specifier = ">=4.2.0" }, { name = "pre-commit", specifier = ">=4.3.0" },
{ name = "pyaspeller", specifier = ">=2.0.2" }, { name = "pyaspeller", specifier = ">=2.0.2" },
{ name = "pyright", specifier = ">=1.1.400" }, { name = "pyright", specifier = ">=1.1.403" },
] ]
[[package]] [[package]]
name = "pre-commit" name = "pre-commit"
version = "4.2.0" version = "4.3.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "cfgv" }, { name = "cfgv" },
@@ -528,9 +564,9 @@ dependencies = [
{ name = "pyyaml" }, { name = "pyyaml" },
{ name = "virtualenv" }, { name = "virtualenv" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/08/39/679ca9b26c7bb2999ff122d50faa301e49af82ca9c066ec061cfbc0c6784/pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146", size = 193424 } sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707 }, { url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965 },
] ]
[[package]] [[package]]