sticker-convert 2.13.2.2__tar.gz → 2.13.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. {sticker_convert-2.13.2.2/src/sticker_convert.egg-info → sticker_convert-2.13.3.0}/PKG-INFO +41 -24
  2. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/README.md +35 -18
  3. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/requirements.txt +5 -5
  4. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/cli.py +10 -2
  5. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/converter.py +45 -17
  6. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui.py +2 -0
  7. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/frames/comp_frame.py +1 -0
  8. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/windows/advanced_compression_window.py +38 -13
  9. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/job.py +10 -4
  10. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/job_option.py +2 -0
  11. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/resources/NotoColorEmoji.ttf +0 -0
  12. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/resources/compression.json +32 -16
  13. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/resources/help.json +4 -1
  14. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/auth/get_kakao_desktop_auth.py +4 -0
  15. sticker_convert-2.13.3.0/src/sticker_convert/version.py +3 -0
  16. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0/src/sticker_convert.egg-info}/PKG-INFO +41 -24
  17. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert.egg-info/requires.txt +5 -5
  18. sticker_convert-2.13.2.2/src/sticker_convert/version.py +0 -3
  19. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/LICENSE +0 -0
  20. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/MANIFEST.in +0 -0
  21. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/pyproject.toml +0 -0
  22. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/setup.cfg +0 -0
  23. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/__init__.py +0 -0
  24. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/__main__.py +0 -0
  25. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/definitions.py +0 -0
  26. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/downloaders/__init__.py +0 -0
  27. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/downloaders/download_band.py +0 -0
  28. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/downloaders/download_base.py +0 -0
  29. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/downloaders/download_discord.py +0 -0
  30. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/downloaders/download_kakao.py +0 -0
  31. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/downloaders/download_line.py +0 -0
  32. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/downloaders/download_ogq.py +0 -0
  33. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/downloaders/download_signal.py +0 -0
  34. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/downloaders/download_telegram.py +0 -0
  35. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/downloaders/download_viber.py +0 -0
  36. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/__init__.py +0 -0
  37. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/frames/__init__.py +0 -0
  38. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/frames/config_frame.py +0 -0
  39. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/frames/control_frame.py +0 -0
  40. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/frames/cred_frame.py +0 -0
  41. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/frames/input_frame.py +0 -0
  42. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/frames/output_frame.py +0 -0
  43. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/frames/progress_frame.py +0 -0
  44. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/frames/right_clicker.py +0 -0
  45. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/gui_utils.py +0 -0
  46. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/windows/__init__.py +0 -0
  47. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/windows/base_window.py +0 -0
  48. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/windows/discord_get_auth_window.py +0 -0
  49. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/windows/kakao_get_auth_window.py +0 -0
  50. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/windows/line_get_auth_window.py +0 -0
  51. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/windows/signal_get_auth_window.py +0 -0
  52. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/gui_components/windows/viber_get_auth_window.py +0 -0
  53. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/.github/FUNDING.yml +0 -0
  54. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/.gitignore +0 -0
  55. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/README.md +0 -0
  56. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers/Info.plist +0 -0
  57. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Info.plist +0 -0
  58. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Contents.json +0 -0
  59. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Contents.json +0 -0
  60. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 1.sticker/Contents.json +0 -0
  61. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 1.sticker/Sticker 1.png +0 -0
  62. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 2.sticker/Contents.json +0 -0
  63. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 2.sticker/Sticker 2.png +0 -0
  64. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 3.sticker/Contents.json +0 -0
  65. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 3.sticker/Sticker 3.png +0 -0
  66. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/App-Store-1024x1024pt.png +0 -0
  67. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Contents.json +0 -0
  68. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-App-Store-1024x768pt.png +0 -0
  69. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPad-67x50pt@2x.png +0 -0
  70. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPad-Pro-74x55pt@2x.png +0 -0
  71. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPhone-60x45pt@2x.png +0 -0
  72. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPhone-60x45pt@3x.png +0 -0
  73. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages27x20pt@2x.png +0 -0
  74. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages27x20pt@3x.png +0 -0
  75. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages32x24pt@2x.png +0 -0
  76. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages32x24pt@3x.png +0 -0
  77. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPad-Settings-29pt@2x.png +0 -0
  78. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPhone-Settings-29pt@3x.png +0 -0
  79. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPhone-settings-29pt@2x.png +0 -0
  80. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.pbxproj +0 -0
  81. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
  82. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -0
  83. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/xcuserdata/niklaspeterson.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  84. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/ios-message-stickers-template/stickers.xcodeproj/xcuserdata/niklaspeterson.xcuserdatad/xcschemes/xcschememanagement.plist +0 -0
  85. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/resources/appicon.icns +0 -0
  86. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/resources/appicon.ico +0 -0
  87. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/resources/appicon.png +0 -0
  88. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/resources/emoji.json +0 -0
  89. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/resources/input.json +0 -0
  90. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/resources/memdump_linux.sh +0 -0
  91. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/resources/memdump_windows.ps1 +0 -0
  92. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/resources/output.json +0 -0
  93. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/uploaders/__init__.py +0 -0
  94. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/uploaders/compress_wastickers.py +0 -0
  95. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/uploaders/upload_base.py +0 -0
  96. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/uploaders/upload_signal.py +0 -0
  97. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/uploaders/upload_telegram.py +0 -0
  98. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/uploaders/upload_viber.py +0 -0
  99. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/uploaders/xcode_imessage.py +0 -0
  100. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/auth/get_discord_auth.py +0 -0
  101. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/auth/get_kakao_auth.py +0 -0
  102. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/auth/get_line_auth.py +0 -0
  103. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/auth/get_signal_auth.py +0 -0
  104. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/auth/get_viber_auth.py +0 -0
  105. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/auth/telegram_api.py +0 -0
  106. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/auth/telethon_setup.py +0 -0
  107. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/callback.py +0 -0
  108. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/chrome_remotedebug.py +0 -0
  109. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/chromiums/linux.py +0 -0
  110. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/chromiums/osx.py +0 -0
  111. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/chromiums/windows.py +0 -0
  112. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/emoji.py +0 -0
  113. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/files/cache_store.py +0 -0
  114. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/files/json_manager.py +0 -0
  115. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/files/json_resources_loader.py +0 -0
  116. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/files/metadata_handler.py +0 -0
  117. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/files/run_bin.py +0 -0
  118. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/files/sanitize_filename.py +0 -0
  119. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/media/apple_png_normalize.py +0 -0
  120. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/media/codec_info.py +0 -0
  121. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/media/decrypt_kakao.py +0 -0
  122. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/media/format_verify.py +0 -0
  123. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/process.py +0 -0
  124. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/singletons.py +0 -0
  125. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert/utils/url_detect.py +0 -0
  126. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert.egg-info/SOURCES.txt +0 -0
  127. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert.egg-info/dependency_links.txt +0 -0
  128. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert.egg-info/entry_points.txt +0 -0
  129. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/src/sticker_convert.egg-info/top_level.txt +0 -0
  130. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/tests/test_compression.py +0 -0
  131. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/tests/test_download.py +0 -0
  132. {sticker_convert-2.13.2.2 → sticker_convert-2.13.3.0}/tests/test_export.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sticker-convert
3
- Version: 2.13.2.2
3
+ Version: 2.13.3.0
4
4
  Summary: Convert (animated) stickers to/from WhatsApp, Telegram, Signal, Line, Kakao, Viber, Discord, iMessage. Written in Python.
5
5
  Author-email: laggykiller <chaudominic2@gmail.com>
6
6
  Maintainer-email: laggykiller <chaudominic2@gmail.com>
@@ -364,22 +364,22 @@ Requires-Python: >=3.9
364
364
  Description-Content-Type: text/markdown
365
365
  License-File: LICENSE
366
366
  Requires-Dist: aiolimiter~=1.2.1
367
- Requires-Dist: anyio~=4.9.0
367
+ Requires-Dist: anyio~=4.10.0
368
368
  Requires-Dist: apngasm_python~=1.3.2
369
369
  Requires-Dist: av>=13.1.0
370
370
  Requires-Dist: beautifulsoup4~=4.13.4
371
- Requires-Dist: cryptg~=0.5.0.post0
371
+ Requires-Dist: cryptg~=0.5.1
372
372
  Requires-Dist: rookiepy~=0.5.6
373
373
  Requires-Dist: httpx~=0.28.1
374
374
  Requires-Dist: imagequant~=1.1.4
375
375
  Requires-Dist: memory-tempfile~=2.2.3
376
376
  Requires-Dist: mergedeep~=1.3.4
377
377
  Requires-Dist: numpy>=1.22.4
378
- Requires-Dist: Pillow~=11.2.1
378
+ Requires-Dist: Pillow~=11.3.0
379
379
  Requires-Dist: pyoxipng~=9.1.0
380
- Requires-Dist: python-telegram-bot~=22.1
380
+ Requires-Dist: python-telegram-bot~=22.3
381
381
  Requires-Dist: psutil~=7.0.0
382
- Requires-Dist: PyMemoryEditor~=1.5.22
382
+ Requires-Dist: PyMemoryEditor~=1.5.24
383
383
  Requires-Dist: requests~=2.32.4
384
384
  Requires-Dist: rlottie_python~=1.3.7
385
385
  Requires-Dist: signalstickers-client-fork-laggykiller~=3.3.0.post2
@@ -497,27 +497,37 @@ Dynamic: license-file
497
497
  To run in CLI mode, pass on any arguments
498
498
 
499
499
  ```
500
- usage: sticker-convert.py [-h] [--version] [--no-confirm] [--no-progress] [--custom-presets CUSTOM_PRESETS] [--input-dir INPUT_DIR]
500
+ usage: sticker-convert.py [-h] [--version] [--no-confirm] [--no-progress]
501
+ [--custom-presets CUSTOM_PRESETS] [--input-dir INPUT_DIR]
501
502
  [--download-auto DOWNLOAD_AUTO | --download-signal DOWNLOAD_SIGNAL | --download-telegram DOWNLOAD_TELEGRAM | --download-telegram-telethon DOWNLOAD_TELEGRAM_TELETHON | --download-line DOWNLOAD_LINE | --download-kakao DOWNLOAD_KAKAO | --download-band DOWNLOAD_BAND | --download-ogq DOWNLOAD_OGQ | --download-viber DOWNLOAD_VIBER | --download-discord DOWNLOAD_DISCORD | --download-discord-emoji DOWNLOAD_DISCORD_EMOJI]
502
503
  [--output-dir OUTPUT_DIR] [--author AUTHOR] [--title TITLE]
503
504
  [--export-signal | --export-telegram | --export-telegram-emoji | --export-telegram-telethon | --export-telegram-emoji-telethon | --export-viber | --export-whatsapp | --export-imessage]
504
505
  [--no-compress]
505
506
  [--preset {auto,signal,telegram,telegram_emoji,whatsapp,line,kakao,band,ogq,viber,discord,discord_emoji,imessage_small,imessage_medium,imessage_large,custom}]
506
- [--steps STEPS] [--processes PROCESSES] [--fps-min FPS_MIN] [--fps-max FPS_MAX] [--fps-power FPS_POWER]
507
- [--res-min RES_MIN] [--res-max RES_MAX] [--res-w-min RES_W_MIN] [--res-w-max RES_W_MAX] [--res-h-min RES_H_MIN]
508
- [--res-h-max RES_H_MAX] [--res-power RES_POWER] [--quality-min QUALITY_MIN] [--quality-max QUALITY_MAX]
509
- [--quality-power QUALITY_POWER] [--color-min COLOR_MIN] [--color-max COLOR_MAX] [--color-power COLOR_POWER]
510
- [--duration-min DURATION_MIN] [--duration-max DURATION_MAX] [--padding-percent PADDING_PERCENT]
511
- [--bg-color BG_COLOR] [--vid-size-max VID_SIZE_MAX] [--img-size-max IMG_SIZE_MAX] [--vid-format VID_FORMAT]
512
- [--img-format IMG_FORMAT] [--fake-vid] [--scale-filter SCALE_FILTER] [--quantize-method QUANTIZE_METHOD]
513
- [--cache-dir CACHE_DIR] [--chromium-path CHROMIUM_PATH] [--default-emoji DEFAULT_EMOJI] [--signal-uuid SIGNAL_UUID]
514
- [--signal-password SIGNAL_PASSWORD] [--signal-get-auth] [--telegram-token TELEGRAM_TOKEN]
515
- [--telegram-userid TELEGRAM_USERID] [--telethon-setup] [--kakao-auth-token KAKAO_AUTH_TOKEN] [--kakao-get-auth]
516
- [--kakao-get-auth-desktop] [--kakao-bin-path KAKAO_BIN_PATH] [--kakao-username KAKAO_USERNAME]
517
- [--kakao-password KAKAO_PASSWORD] [--kakao-country-code KAKAO_COUNTRY_CODE]
518
- [--kakao-phone-number KAKAO_PHONE_NUMBER] [--line-get-auth] [--line-cookies LINE_COOKIES] [--viber-auth VIBER_AUTH]
519
- [--viber-get-auth VIBER_GET_AUTH] [--viber-bin-path VIBER_BIN_PATH] [--discord-get-auth]
520
- [--discord-token DISCORD_TOKEN] [--save-cred]
507
+ [--steps STEPS] [--processes PROCESSES] [--fps-min FPS_MIN] [--fps-max FPS_MAX]
508
+ [--fps-power FPS_POWER] [--res-min RES_MIN] [--res-max RES_MAX]
509
+ [--res-w-min RES_W_MIN] [--res-w-max RES_W_MAX] [--res-h-min RES_H_MIN]
510
+ [--res-h-max RES_H_MAX] [--res-power RES_POWER] [--res-snap-pow2]
511
+ [--no-res-snap-pow2] [--quality-min QUALITY_MIN] [--quality-max QUALITY_MAX]
512
+ [--quality-power QUALITY_POWER] [--color-min COLOR_MIN] [--color-max COLOR_MAX]
513
+ [--color-power COLOR_POWER] [--duration-min DURATION_MIN]
514
+ [--duration-max DURATION_MAX] [--padding-percent PADDING_PERCENT]
515
+ [--bg-color BG_COLOR] [--vid-size-max VID_SIZE_MAX]
516
+ [--img-size-max IMG_SIZE_MAX] [--vid-format VID_FORMAT]
517
+ [--img-format IMG_FORMAT] [--fake-vid] [--no-fake-vid]
518
+ [--scale-filter SCALE_FILTER] [--quantize-method QUANTIZE_METHOD]
519
+ [--cache-dir CACHE_DIR] [--chromium-path CHROMIUM_PATH]
520
+ [--default-emoji DEFAULT_EMOJI] [--signal-uuid SIGNAL_UUID]
521
+ [--signal-password SIGNAL_PASSWORD] [--signal-get-auth]
522
+ [--telegram-token TELEGRAM_TOKEN] [--telegram-userid TELEGRAM_USERID]
523
+ [--telethon-setup] [--kakao-auth-token KAKAO_AUTH_TOKEN] [--kakao-get-auth]
524
+ [--kakao-get-auth-desktop] [--kakao-bin-path KAKAO_BIN_PATH]
525
+ [--kakao-username KAKAO_USERNAME] [--kakao-password KAKAO_PASSWORD]
526
+ [--kakao-country-code KAKAO_COUNTRY_CODE]
527
+ [--kakao-phone-number KAKAO_PHONE_NUMBER] [--line-get-auth]
528
+ [--line-cookies LINE_COOKIES] [--viber-auth VIBER_AUTH]
529
+ [--viber-get-auth VIBER_GET_AUTH] [--viber-bin-path VIBER_BIN_PATH]
530
+ [--discord-get-auth] [--discord-token DISCORD_TOKEN] [--save-cred]
521
531
 
522
532
  CLI for stickers-convert
523
533
 
@@ -619,6 +629,9 @@ Compression options:
619
629
  Between -1 and positive infinity.
620
630
  Power lower = More importance of the parameter,
621
631
  try harder to keep higher and not sacrifice.
632
+ --res-snap-pow2 Snap resolution to nearest power of 2 (1,2,4,8,16,...).
633
+ Ignored if no power of 2 present between minimum and maximum resolutions
634
+ --no-res-snap-pow2 Disable res_snap_pow2
622
635
  --quality-min QUALITY_MIN
623
636
  Set minimum quality.
624
637
  --quality-max QUALITY_MAX
@@ -653,6 +666,7 @@ Compression options:
653
666
  Useful if:
654
667
  (1) Size limit for video is larger than image;
655
668
  (2) Mix image and video into same pack.
669
+ --no-fake-vid Disable fake_vid
656
670
  --scale-filter SCALE_FILTER
657
671
  Set scale filter. Default as bicubic. Valid options are:
658
672
  - nearest = Use nearest neighbour (Suitable for pixel art)
@@ -663,9 +677,12 @@ Compression options:
663
677
  - lanczos = A high-quality downsampling filter
664
678
  --quantize-method QUANTIZE_METHOD
665
679
  Set method for quantizing image. Default as imagequant. Valid options are:
666
- - imagequant = Best quality but slow
667
- - fastoctree = Fast but image looks chunky
680
+ - imagequant = Speed+ Compression+ Quality++++ RGBA Supported
681
+ - fastoctree = Speed++, Compression++++ Quality+ RGBA Supported
682
+ - maxcoverage = Speed+++, Compression+++ Quality++ RGBA unsupported
683
+ - mediancut = Speed++++ Compression++ Quality+++ RGBA unsupported
668
684
  - none = No image quantizing, large image size as result
685
+ Lower quality would make image chunky
669
686
  --cache-dir CACHE_DIR
670
687
  Set custom cache directory.
671
688
  Useful for debugging, or speed up conversion if cache_dir is on RAM disk.
@@ -105,27 +105,37 @@
105
105
  To run in CLI mode, pass on any arguments
106
106
 
107
107
  ```
108
- usage: sticker-convert.py [-h] [--version] [--no-confirm] [--no-progress] [--custom-presets CUSTOM_PRESETS] [--input-dir INPUT_DIR]
108
+ usage: sticker-convert.py [-h] [--version] [--no-confirm] [--no-progress]
109
+ [--custom-presets CUSTOM_PRESETS] [--input-dir INPUT_DIR]
109
110
  [--download-auto DOWNLOAD_AUTO | --download-signal DOWNLOAD_SIGNAL | --download-telegram DOWNLOAD_TELEGRAM | --download-telegram-telethon DOWNLOAD_TELEGRAM_TELETHON | --download-line DOWNLOAD_LINE | --download-kakao DOWNLOAD_KAKAO | --download-band DOWNLOAD_BAND | --download-ogq DOWNLOAD_OGQ | --download-viber DOWNLOAD_VIBER | --download-discord DOWNLOAD_DISCORD | --download-discord-emoji DOWNLOAD_DISCORD_EMOJI]
110
111
  [--output-dir OUTPUT_DIR] [--author AUTHOR] [--title TITLE]
111
112
  [--export-signal | --export-telegram | --export-telegram-emoji | --export-telegram-telethon | --export-telegram-emoji-telethon | --export-viber | --export-whatsapp | --export-imessage]
112
113
  [--no-compress]
113
114
  [--preset {auto,signal,telegram,telegram_emoji,whatsapp,line,kakao,band,ogq,viber,discord,discord_emoji,imessage_small,imessage_medium,imessage_large,custom}]
114
- [--steps STEPS] [--processes PROCESSES] [--fps-min FPS_MIN] [--fps-max FPS_MAX] [--fps-power FPS_POWER]
115
- [--res-min RES_MIN] [--res-max RES_MAX] [--res-w-min RES_W_MIN] [--res-w-max RES_W_MAX] [--res-h-min RES_H_MIN]
116
- [--res-h-max RES_H_MAX] [--res-power RES_POWER] [--quality-min QUALITY_MIN] [--quality-max QUALITY_MAX]
117
- [--quality-power QUALITY_POWER] [--color-min COLOR_MIN] [--color-max COLOR_MAX] [--color-power COLOR_POWER]
118
- [--duration-min DURATION_MIN] [--duration-max DURATION_MAX] [--padding-percent PADDING_PERCENT]
119
- [--bg-color BG_COLOR] [--vid-size-max VID_SIZE_MAX] [--img-size-max IMG_SIZE_MAX] [--vid-format VID_FORMAT]
120
- [--img-format IMG_FORMAT] [--fake-vid] [--scale-filter SCALE_FILTER] [--quantize-method QUANTIZE_METHOD]
121
- [--cache-dir CACHE_DIR] [--chromium-path CHROMIUM_PATH] [--default-emoji DEFAULT_EMOJI] [--signal-uuid SIGNAL_UUID]
122
- [--signal-password SIGNAL_PASSWORD] [--signal-get-auth] [--telegram-token TELEGRAM_TOKEN]
123
- [--telegram-userid TELEGRAM_USERID] [--telethon-setup] [--kakao-auth-token KAKAO_AUTH_TOKEN] [--kakao-get-auth]
124
- [--kakao-get-auth-desktop] [--kakao-bin-path KAKAO_BIN_PATH] [--kakao-username KAKAO_USERNAME]
125
- [--kakao-password KAKAO_PASSWORD] [--kakao-country-code KAKAO_COUNTRY_CODE]
126
- [--kakao-phone-number KAKAO_PHONE_NUMBER] [--line-get-auth] [--line-cookies LINE_COOKIES] [--viber-auth VIBER_AUTH]
127
- [--viber-get-auth VIBER_GET_AUTH] [--viber-bin-path VIBER_BIN_PATH] [--discord-get-auth]
128
- [--discord-token DISCORD_TOKEN] [--save-cred]
115
+ [--steps STEPS] [--processes PROCESSES] [--fps-min FPS_MIN] [--fps-max FPS_MAX]
116
+ [--fps-power FPS_POWER] [--res-min RES_MIN] [--res-max RES_MAX]
117
+ [--res-w-min RES_W_MIN] [--res-w-max RES_W_MAX] [--res-h-min RES_H_MIN]
118
+ [--res-h-max RES_H_MAX] [--res-power RES_POWER] [--res-snap-pow2]
119
+ [--no-res-snap-pow2] [--quality-min QUALITY_MIN] [--quality-max QUALITY_MAX]
120
+ [--quality-power QUALITY_POWER] [--color-min COLOR_MIN] [--color-max COLOR_MAX]
121
+ [--color-power COLOR_POWER] [--duration-min DURATION_MIN]
122
+ [--duration-max DURATION_MAX] [--padding-percent PADDING_PERCENT]
123
+ [--bg-color BG_COLOR] [--vid-size-max VID_SIZE_MAX]
124
+ [--img-size-max IMG_SIZE_MAX] [--vid-format VID_FORMAT]
125
+ [--img-format IMG_FORMAT] [--fake-vid] [--no-fake-vid]
126
+ [--scale-filter SCALE_FILTER] [--quantize-method QUANTIZE_METHOD]
127
+ [--cache-dir CACHE_DIR] [--chromium-path CHROMIUM_PATH]
128
+ [--default-emoji DEFAULT_EMOJI] [--signal-uuid SIGNAL_UUID]
129
+ [--signal-password SIGNAL_PASSWORD] [--signal-get-auth]
130
+ [--telegram-token TELEGRAM_TOKEN] [--telegram-userid TELEGRAM_USERID]
131
+ [--telethon-setup] [--kakao-auth-token KAKAO_AUTH_TOKEN] [--kakao-get-auth]
132
+ [--kakao-get-auth-desktop] [--kakao-bin-path KAKAO_BIN_PATH]
133
+ [--kakao-username KAKAO_USERNAME] [--kakao-password KAKAO_PASSWORD]
134
+ [--kakao-country-code KAKAO_COUNTRY_CODE]
135
+ [--kakao-phone-number KAKAO_PHONE_NUMBER] [--line-get-auth]
136
+ [--line-cookies LINE_COOKIES] [--viber-auth VIBER_AUTH]
137
+ [--viber-get-auth VIBER_GET_AUTH] [--viber-bin-path VIBER_BIN_PATH]
138
+ [--discord-get-auth] [--discord-token DISCORD_TOKEN] [--save-cred]
129
139
 
130
140
  CLI for stickers-convert
131
141
 
@@ -227,6 +237,9 @@ Compression options:
227
237
  Between -1 and positive infinity.
228
238
  Power lower = More importance of the parameter,
229
239
  try harder to keep higher and not sacrifice.
240
+ --res-snap-pow2 Snap resolution to nearest power of 2 (1,2,4,8,16,...).
241
+ Ignored if no power of 2 present between minimum and maximum resolutions
242
+ --no-res-snap-pow2 Disable res_snap_pow2
230
243
  --quality-min QUALITY_MIN
231
244
  Set minimum quality.
232
245
  --quality-max QUALITY_MAX
@@ -261,6 +274,7 @@ Compression options:
261
274
  Useful if:
262
275
  (1) Size limit for video is larger than image;
263
276
  (2) Mix image and video into same pack.
277
+ --no-fake-vid Disable fake_vid
264
278
  --scale-filter SCALE_FILTER
265
279
  Set scale filter. Default as bicubic. Valid options are:
266
280
  - nearest = Use nearest neighbour (Suitable for pixel art)
@@ -271,9 +285,12 @@ Compression options:
271
285
  - lanczos = A high-quality downsampling filter
272
286
  --quantize-method QUANTIZE_METHOD
273
287
  Set method for quantizing image. Default as imagequant. Valid options are:
274
- - imagequant = Best quality but slow
275
- - fastoctree = Fast but image looks chunky
288
+ - imagequant = Speed+ Compression+ Quality++++ RGBA Supported
289
+ - fastoctree = Speed++, Compression++++ Quality+ RGBA Supported
290
+ - maxcoverage = Speed+++, Compression+++ Quality++ RGBA unsupported
291
+ - mediancut = Speed++++ Compression++ Quality+++ RGBA unsupported
276
292
  - none = No image quantizing, large image size as result
293
+ Lower quality would make image chunky
277
294
  --cache-dir CACHE_DIR
278
295
  Set custom cache directory.
279
296
  Useful for debugging, or speed up conversion if cache_dir is on RAM disk.
@@ -1,20 +1,20 @@
1
1
  aiolimiter~=1.2.1
2
- anyio~=4.9.0
2
+ anyio~=4.10.0
3
3
  apngasm_python~=1.3.2
4
4
  av>=13.1.0
5
5
  beautifulsoup4~=4.13.4
6
- cryptg~=0.5.0.post0
6
+ cryptg~=0.5.1
7
7
  rookiepy~=0.5.6
8
8
  httpx~=0.28.1
9
9
  imagequant~=1.1.4
10
10
  memory-tempfile~=2.2.3
11
11
  mergedeep~=1.3.4
12
12
  numpy>=1.22.4
13
- Pillow~=11.2.1
13
+ Pillow~=11.3.0
14
14
  pyoxipng~=9.1.0
15
- python-telegram-bot~=22.1
15
+ python-telegram-bot~=22.3
16
16
  psutil~=7.0.0
17
- PyMemoryEditor~=1.5.22
17
+ PyMemoryEditor~=1.5.24
18
18
  requests~=2.32.4
19
19
  rlottie_python~=1.3.7
20
20
  signalstickers-client-fork-laggykiller~=3.3.0.post2
@@ -141,7 +141,12 @@ class CLI:
141
141
  "quantize_method",
142
142
  "chromium_path",
143
143
  )
144
- flags_comp_bool = ("fake_vid",)
144
+ flags_comp_bool = (
145
+ "fake_vid",
146
+ "no_fake_vid",
147
+ "res_snap_pow2",
148
+ "no_res_snap_pow2",
149
+ )
145
150
  keyword_args: Dict[str, Any]
146
151
  for k, v in self.help["comp"].items():
147
152
  if k in flags_comp_int:
@@ -394,6 +399,9 @@ class CLI:
394
399
  res_power=self.compression_presets[preset]["res"]["power"]
395
400
  if args.res_power is None
396
401
  else args.res_power,
402
+ res_snap_pow2=self.compression_presets[preset]["res"]["snap_pow2"]
403
+ if args.res_snap_pow2 is None and args.no_res_snap_pow2 is None
404
+ else args.res_snap_pow2,
397
405
  quality_min=self.compression_presets[preset]["quality"]["min"]
398
406
  if args.quality_min is None
399
407
  else args.quality_min,
@@ -428,7 +436,7 @@ class CLI:
428
436
  if args.steps is None
429
437
  else args.steps,
430
438
  fake_vid=self.compression_presets[preset]["fake_vid"]
431
- if args.fake_vid is None
439
+ if args.fake_vid is None and args.no_fake_vid is None
432
440
  else args.fake_vid,
433
441
  chromium_path=args.chromium_path,
434
442
  cache_dir=args.cache_dir,
@@ -3,7 +3,7 @@ import json
3
3
  import os
4
4
  from fractions import Fraction
5
5
  from io import BytesIO
6
- from math import ceil, floor
6
+ from math import ceil, floor, log2
7
7
  from pathlib import Path
8
8
  from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Union, cast
9
9
 
@@ -57,6 +57,7 @@ def get_step_value(
57
57
  steps: int,
58
58
  power: float = 1.0,
59
59
  even: bool = False,
60
+ snap_pow2: bool = False,
60
61
  ) -> Optional[int]:
61
62
  # Power should be between -1 and positive infinity
62
63
  # Smaller power = More 'importance' of the parameter
@@ -70,6 +71,15 @@ def get_step_value(
70
71
 
71
72
  if max_step is not None and min_step is not None:
72
73
  v = round((max_step - min_step) * step / steps * factor + min_step)
74
+ if snap_pow2 is True and floor(log2(max_step)) >= ceil(log2(min_step)):
75
+ lower_exp = max(floor(log2(v)), ceil(log2(min_step)))
76
+ lower_pow2 = 2**lower_exp
77
+ upper_exp = min(ceil(log2(v)), floor(log2(max_step)))
78
+ upper_pow2 = 2**upper_exp
79
+ if abs(v - lower_pow2) <= abs(v - upper_pow2):
80
+ return lower_pow2
81
+ else:
82
+ return upper_pow2
73
83
  if even is True and v % 2 == 1:
74
84
  return v + 1
75
85
  return v
@@ -342,6 +352,7 @@ class StickerConvert:
342
352
  self.opt_comp.steps,
343
353
  self.opt_comp.res_power,
344
354
  need_even,
355
+ self.opt_comp.res_snap_pow2,
345
356
  ),
346
357
  get_step_value(
347
358
  self.opt_comp.res_h_max,
@@ -350,6 +361,7 @@ class StickerConvert:
350
361
  self.opt_comp.steps,
351
362
  self.opt_comp.res_power,
352
363
  need_even,
364
+ self.opt_comp.res_snap_pow2,
353
365
  ),
354
366
  get_step_value(
355
367
  self.opt_comp.quality_max,
@@ -932,13 +944,19 @@ class StickerConvert:
932
944
  self.tmp_f.write(frame_optimized)
933
945
 
934
946
  def _frames_export_apng(self) -> None:
935
- from apngasm_python._apngasm_python import APNGAsm, create_frame_from_rgba # type: ignore
947
+ from apngasm_python._apngasm_python import APNGAsm, create_frame_from_rgb, create_frame_from_rgba # type: ignore
936
948
 
937
949
  assert self.fps
938
950
  assert self.res_h
939
951
 
940
952
  frames_concat = np.concatenate(self.frames_processed)
941
953
  with Image.fromarray(frames_concat, "RGBA") as image_concat: # type: ignore
954
+ if image_concat.getextrema()[3][0] < 255: # type: ignore
955
+ mode = "RGBA"
956
+ create_frame_method = create_frame_from_rgba
957
+ else:
958
+ mode = "RGB"
959
+ create_frame_method = create_frame_from_rgb
942
960
  image_quant = self.quantize(image_concat)
943
961
 
944
962
  if self.apngasm is None:
@@ -947,15 +965,10 @@ class StickerConvert:
947
965
 
948
966
  delay_num = int(1000 / self.fps)
949
967
  for i in range(0, image_quant.height, self.res_h):
950
- with BytesIO() as f:
951
- crop_dimension = (0, i, image_quant.width, i + self.res_h)
952
- image_cropped = image_quant.crop(crop_dimension)
953
- image_cropped.save(f, format="png")
954
- f.seek(0)
955
- frame_optimized = self.optimize_png(f.read())
956
- with Image.open(BytesIO(frame_optimized)) as im:
957
- image_final = im.convert("RGBA")
958
- frame_final = create_frame_from_rgba(
968
+ crop_dimension = (0, i, image_quant.width, i + self.res_h)
969
+ image_cropped = image_quant.crop(crop_dimension)
970
+ image_final = image_cropped.convert(mode)
971
+ frame_final = create_frame_method(
959
972
  np.array(image_final),
960
973
  width=image_final.width,
961
974
  height=image_final.height,
@@ -969,7 +982,8 @@ class StickerConvert:
969
982
  self.apngasm.assemble(tmp_apng.as_posix())
970
983
 
971
984
  with open(tmp_apng, "rb") as f:
972
- self.tmp_f.write(f.read())
985
+ apng_optimized = self.optimize_png(f.read())
986
+ self.tmp_f.write(apng_optimized)
973
987
 
974
988
  self.apngasm.reset()
975
989
 
@@ -978,7 +992,7 @@ class StickerConvert:
978
992
 
979
993
  return oxipng.optimize_from_memory(
980
994
  image_bytes,
981
- level=4,
995
+ level=6,
982
996
  fix_errors=True,
983
997
  filter=[oxipng.RowFilter.Brute],
984
998
  optimize_alpha=True,
@@ -990,8 +1004,8 @@ class StickerConvert:
990
1004
  return image.copy()
991
1005
  if self.opt_comp.quantize_method == "imagequant":
992
1006
  return self._quantize_by_imagequant(image)
993
- if self.opt_comp.quantize_method == "fastoctree":
994
- return self._quantize_by_fastoctree(image)
1007
+ if self.opt_comp.quantize_method in ("mediancut", "maxcoverage", "fastoctree"):
1008
+ return self._quantize_by_pillow(image)
995
1009
 
996
1010
  return image
997
1011
 
@@ -1022,10 +1036,24 @@ class StickerConvert:
1022
1036
 
1023
1037
  return image
1024
1038
 
1025
- def _quantize_by_fastoctree(self, image: Image.Image) -> Image.Image:
1039
+ def _quantize_by_pillow(self, image: Image.Image) -> Image.Image:
1026
1040
  assert self.color
1027
1041
 
1028
- return image.quantize(colors=self.color, method=2)
1042
+ if image.mode == "RGBA" and self.opt_comp.quantize_method in (
1043
+ "mediancut",
1044
+ "maxcoverage",
1045
+ ):
1046
+ self.cb.put(
1047
+ f"[W] {self.opt_comp.quantize_method} does not support RGBA, defaulted to fastoctree quantization"
1048
+ )
1049
+ method = Image.Quantize.FASTOCTREE
1050
+ elif self.opt_comp.quantize_method == "mediancut":
1051
+ method = Image.Quantize.MEDIANCUT
1052
+ elif self.opt_comp.quantize_method == "maxcoverage":
1053
+ method = Image.Quantize.MAXCOVERAGE
1054
+ else:
1055
+ method = Image.Quantize.FASTOCTREE
1056
+ return image.quantize(colors=self.color, method=method)
1029
1057
 
1030
1058
  def fix_fps(self, fps: float) -> Fraction:
1031
1059
  # After rounding fps/duration during export,
@@ -116,6 +116,7 @@ class GUI(Window):
116
116
  self.res_h_max_var = IntVar(self)
117
117
  self.res_h_disable_var = BooleanVar()
118
118
  self.res_power_var = DoubleVar()
119
+ self.res_snap_pow2_var = BooleanVar()
119
120
  self.quality_min_var = IntVar(self)
120
121
  self.quality_max_var = IntVar(self)
121
122
  self.quality_disable_var = BooleanVar()
@@ -547,6 +548,7 @@ class GUI(Window):
547
548
  if not self.res_h_disable_var.get()
548
549
  else None,
549
550
  res_power=self.res_power_var.get(),
551
+ res_snap_pow2=self.res_snap_pow2_var.get(),
550
552
  quality_min=self.quality_min_var.get()
551
553
  if not self.quality_disable_var.get()
552
554
  else None,
@@ -119,6 +119,7 @@ class CompFrame(LabelFrame):
119
119
  self.gui.res_h_min_var.set(preset.get("res", {}).get("h", {}).get("min"))
120
120
  self.gui.res_h_max_var.set(preset.get("res", {}).get("h", {}).get("max"))
121
121
  self.gui.res_power_var.set(preset.get("res", {}).get("power"))
122
+ self.gui.res_snap_pow2_var.set(preset.get("res", {}).get("snap_pow2"))
122
123
  self.gui.quality_min_var.set(preset.get("quality", {}).get("min"))
123
124
  self.gui.quality_max_var.set(preset.get("quality", {}).get("max"))
124
125
  self.gui.quality_power_var.set(preset.get("quality", {}).get("power"))
@@ -15,7 +15,6 @@ from sticker_convert.gui_components.windows.base_window import BaseWindow
15
15
 
16
16
 
17
17
  class AdvancedCompressionWindow(BaseWindow):
18
- emoji_column_per_row = 8
19
18
  emoji_visible_rows = 5
20
19
  emoji_btns: List[Tuple[Button, ImageTk.PhotoImage]] = []
21
20
 
@@ -101,6 +100,20 @@ class AdvancedCompressionWindow(BaseWindow):
101
100
  bootstyle="danger-round-toggle", # type: ignore
102
101
  )
103
102
 
103
+ self.res_snap_pow2_help_btn = self.add_help_btn(
104
+ self.gui.help["comp"]["res_snap_pow2"]
105
+ )
106
+ self.res_snap_pow2_lbl = Label(
107
+ self.frame_advcomp, text="Snap resolution to power of 2"
108
+ )
109
+ self.res_snap_pow2_cbox = Checkbutton(
110
+ self.frame_advcomp,
111
+ variable=self.gui.res_snap_pow2_var,
112
+ onvalue=True,
113
+ offvalue=False,
114
+ bootstyle="success-round-toggle", # type: ignore
115
+ )
116
+
104
117
  self.quality_help_btn = self.add_help_btn(self.gui.help["comp"]["quality"])
105
118
  self.quality_lbl = Label(self.frame_advcomp, text="Output quality (0-100)")
106
119
  self.quality_min_lbl = Label(self.frame_advcomp, text="Min:")
@@ -289,6 +302,8 @@ class AdvancedCompressionWindow(BaseWindow):
289
302
  self.gui.quantize_method_var.get(),
290
303
  "imagequant",
291
304
  "fastoctree",
305
+ "maxcoverage",
306
+ "mediancut",
292
307
  "none",
293
308
  bootstyle="secondary", # type: ignore
294
309
  )
@@ -326,7 +341,7 @@ class AdvancedCompressionWindow(BaseWindow):
326
341
  self.fps_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
327
342
  self.fps_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
328
343
  self.fps_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
329
- self.fps_disable_cbox.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
344
+ self.fps_disable_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
330
345
  r += 1
331
346
  self.res_w_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
332
347
  self.res_w_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
@@ -334,7 +349,7 @@ class AdvancedCompressionWindow(BaseWindow):
334
349
  self.res_w_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
335
350
  self.res_w_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
336
351
  self.res_w_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
337
- self.res_w_disable_cbox.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
352
+ self.res_w_disable_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
338
353
  r += 1
339
354
  self.res_h_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
340
355
  self.res_h_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
@@ -342,7 +357,11 @@ class AdvancedCompressionWindow(BaseWindow):
342
357
  self.res_h_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
343
358
  self.res_h_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
344
359
  self.res_h_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
345
- self.res_h_disable_cbox.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
360
+ self.res_h_disable_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
361
+ r += 1
362
+ self.res_snap_pow2_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
363
+ self.res_snap_pow2_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
364
+ self.res_snap_pow2_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
346
365
  r += 1
347
366
  self.quality_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
348
367
  self.quality_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
@@ -350,7 +369,7 @@ class AdvancedCompressionWindow(BaseWindow):
350
369
  self.quality_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
351
370
  self.quality_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
352
371
  self.quality_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
353
- self.quality_disable_cbox.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
372
+ self.quality_disable_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
354
373
  r += 1
355
374
  self.color_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
356
375
  self.color_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
@@ -358,7 +377,7 @@ class AdvancedCompressionWindow(BaseWindow):
358
377
  self.color_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
359
378
  self.color_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
360
379
  self.color_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
361
- self.color_disable_cbox.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
380
+ self.color_disable_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
362
381
  r += 1
363
382
  self.duration_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
364
383
  self.duration_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
@@ -366,7 +385,7 @@ class AdvancedCompressionWindow(BaseWindow):
366
385
  self.duration_min_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
367
386
  self.duration_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
368
387
  self.duration_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
369
- self.duration_disable_cbox.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
388
+ self.duration_disable_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
370
389
  r += 1
371
390
  self.size_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
372
391
  self.size_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
@@ -374,14 +393,14 @@ class AdvancedCompressionWindow(BaseWindow):
374
393
  self.img_size_max_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
375
394
  self.vid_size_max_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
376
395
  self.vid_size_max_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
377
- self.size_disable_cbox.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
396
+ self.size_disable_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
378
397
  r += 1
379
398
  self.format_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
380
399
  self.format_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
381
400
  self.img_format_lbl.grid(column=2, row=r, sticky="w", padx=3, pady=3)
382
401
  self.img_format_entry.grid(column=3, row=r, sticky="nes", padx=3, pady=3)
383
402
  self.vid_format_lbl.grid(column=4, row=r, sticky="w", padx=3, pady=3)
384
- self.vid_format_entry.grid(column=5, row=r, sticky="nes", padx=3, pady=3)
403
+ self.vid_format_entry.grid(column=5, row=r, sticky="w", padx=3, pady=3)
385
404
  r += 1
386
405
  self.power_help_btn1.grid(column=0, row=r, sticky="w", padx=3, pady=3)
387
406
  self.power_lbl1.grid(column=1, row=r, sticky="w", padx=3, pady=3)
@@ -404,12 +423,12 @@ class AdvancedCompressionWindow(BaseWindow):
404
423
  r += 1
405
424
  self.fake_vid_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
406
425
  self.fake_vid_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
407
- self.fake_vid_cbox.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
426
+ self.fake_vid_cbox.grid(column=6, row=r, sticky="w", padx=3, pady=3)
408
427
  r += 1
409
428
  self.bg_color_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
410
429
  self.bg_color_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
411
430
  self.bg_color_entry.grid(column=5, row=r, sticky="w", padx=3, pady=3)
412
- self.bg_color_btn.grid(column=6, row=r, sticky="nes", padx=3, pady=3)
431
+ self.bg_color_btn.grid(column=6, row=r, sticky="w", padx=3, pady=3)
413
432
  r += 1
414
433
  self.scale_filter_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
415
434
  self.scale_filter_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
@@ -662,6 +681,7 @@ class AdvancedCompressionWindow(BaseWindow):
662
681
 
663
682
  column = 0
664
683
  row = 0
684
+ emoji_column_per_row = 0
665
685
 
666
686
  self.emoji_btns = []
667
687
  for entry in self.gui.emoji_list:
@@ -702,7 +722,12 @@ class AdvancedCompressionWindow(BaseWindow):
702
722
 
703
723
  column += 1
704
724
 
705
- if column == self.emoji_column_per_row:
725
+ if emoji_column_per_row == 0:
726
+ self.emoji_canvas.update_idletasks()
727
+ emoji_column_per_row = int(
728
+ self.emoji_canvas.winfo_width() / button.winfo_width()
729
+ )
730
+ if column == emoji_column_per_row:
706
731
  column = 0
707
732
  row += 1
708
733
 
@@ -714,7 +739,7 @@ class AdvancedCompressionWindow(BaseWindow):
714
739
  # Resize the canvas frame to show specified number of buttons and the scrollbar
715
740
  if len(self.emoji_btns) > 0:
716
741
  in_view_columns_width = (
717
- self.emoji_btns[0][0].winfo_width() * self.emoji_column_per_row
742
+ self.emoji_btns[0][0].winfo_width() * emoji_column_per_row
718
743
  )
719
744
  in_view_rows_height = (
720
745
  self.emoji_btns[0][0].winfo_height() * self.emoji_visible_rows
@@ -422,16 +422,22 @@ class Job:
422
422
  ):
423
423
  error_msg += "\n"
424
424
  error_msg += (
425
- f"[X] scale_filter {self.opt_comp.scale_filter} is not valid option"
425
+ f"[X] scale_filter {self.opt_comp.scale_filter} is not valid option\n"
426
426
  )
427
427
  error_msg += (
428
428
  " Valid options: nearest, box, bilinear, hamming, bicubic, lanczos"
429
429
  )
430
430
 
431
- if self.opt_comp.quantize_method not in ("imagequant", "fastoctree", "none"):
431
+ if self.opt_comp.quantize_method not in (
432
+ "imagequant",
433
+ "fastoctree",
434
+ "maxcoverage",
435
+ "mediancut",
436
+ "none",
437
+ ):
432
438
  error_msg += "\n"
433
- error_msg += f"[X] quantize_method {self.opt_comp.quantize_method} is not valid option"
434
- error_msg += " Valid options: imagequant, fastoctree, none"
439
+ error_msg += f"[X] quantize_method {self.opt_comp.quantize_method} is not valid option\n"
440
+ error_msg += " Valid options: imagequant, fastoctree, maxcoverage, mediancut, none"
435
441
 
436
442
  if self.opt_comp.bg_color:
437
443
  try:
@@ -55,6 +55,7 @@ class CompOption(BaseOption):
55
55
  res_h_min: Optional[int] = None
56
56
  res_h_max: Optional[int] = None
57
57
  res_power: float = 3.0
58
+ res_snap_pow2: bool = False
58
59
 
59
60
  quality_min: Optional[int] = None
60
61
  quality_max: Optional[int] = None
@@ -92,6 +93,7 @@ class CompOption(BaseOption):
92
93
  "w": {"min": self.res_w_min, "max": self.res_w_max},
93
94
  "h": {"min": self.res_h_min, "max": self.res_h_max},
94
95
  "power": self.res_power,
96
+ "snap_pow2": self.res_snap_pow2,
95
97
  },
96
98
  "quality": {
97
99
  "min": self.quality_min,