sticker-convert 2.8.11__tar.gz → 2.8.13__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. {sticker_convert-2.8.11/src/sticker_convert.egg-info → sticker_convert-2.8.13}/PKG-INFO +5 -4
  2. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/requirements.txt +4 -3
  3. sticker_convert-2.8.13/src/sticker_convert/downloaders/download_kakao.py +406 -0
  4. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/version.py +1 -1
  5. {sticker_convert-2.8.11 → sticker_convert-2.8.13/src/sticker_convert.egg-info}/PKG-INFO +5 -4
  6. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert.egg-info/requires.txt +4 -3
  7. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/tests/test_download.py +14 -0
  8. sticker_convert-2.8.11/src/sticker_convert/downloaders/download_kakao.py +0 -255
  9. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/LICENSE +0 -0
  10. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/MANIFEST.in +0 -0
  11. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/README.md +0 -0
  12. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/pyproject.toml +0 -0
  13. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/setup.cfg +0 -0
  14. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/__init__.py +0 -0
  15. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/__main__.py +0 -0
  16. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/cli.py +0 -0
  17. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/converter.py +0 -0
  18. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/definitions.py +0 -0
  19. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/downloaders/__init__.py +0 -0
  20. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/downloaders/download_base.py +0 -0
  21. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/downloaders/download_line.py +0 -0
  22. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/downloaders/download_signal.py +0 -0
  23. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/downloaders/download_telegram.py +0 -0
  24. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/downloaders/download_viber.py +0 -0
  25. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui.py +0 -0
  26. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui_components/__init__.py +0 -0
  27. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui_components/frames/__init__.py +0 -0
  28. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui_components/frames/comp_frame.py +0 -0
  29. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui_components/frames/config_frame.py +0 -0
  30. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui_components/frames/control_frame.py +0 -0
  31. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui_components/frames/cred_frame.py +0 -0
  32. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui_components/frames/input_frame.py +0 -0
  33. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui_components/frames/output_frame.py +0 -0
  34. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui_components/frames/progress_frame.py +0 -0
  35. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui_components/frames/right_clicker.py +0 -0
  36. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui_components/gui_utils.py +0 -0
  37. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui_components/windows/__init__.py +0 -0
  38. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui_components/windows/advanced_compression_window.py +0 -0
  39. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui_components/windows/base_window.py +0 -0
  40. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui_components/windows/kakao_get_auth_window.py +0 -0
  41. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui_components/windows/line_get_auth_window.py +0 -0
  42. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/gui_components/windows/signal_get_auth_window.py +0 -0
  43. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/.github/FUNDING.yml +0 -0
  44. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/.gitignore +0 -0
  45. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/README.md +0 -0
  46. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers/Info.plist +0 -0
  47. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Info.plist +0 -0
  48. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Contents.json +0 -0
  49. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Contents.json +0 -0
  50. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 1.sticker/Contents.json +0 -0
  51. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 1.sticker/Sticker 1.png +0 -0
  52. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 2.sticker/Contents.json +0 -0
  53. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 2.sticker/Sticker 2.png +0 -0
  54. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 3.sticker/Contents.json +0 -0
  55. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 3.sticker/Sticker 3.png +0 -0
  56. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/App-Store-1024x1024pt.png +0 -0
  57. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Contents.json +0 -0
  58. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-App-Store-1024x768pt.png +0 -0
  59. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPad-67x50pt@2x.png +0 -0
  60. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPad-Pro-74x55pt@2x.png +0 -0
  61. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPhone-60x45pt@2x.png +0 -0
  62. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPhone-60x45pt@3x.png +0 -0
  63. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages27x20pt@2x.png +0 -0
  64. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages27x20pt@3x.png +0 -0
  65. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages32x24pt@2x.png +0 -0
  66. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages32x24pt@3x.png +0 -0
  67. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPad-Settings-29pt@2x.png +0 -0
  68. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPhone-Settings-29pt@3x.png +0 -0
  69. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPhone-settings-29pt@2x.png +0 -0
  70. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.pbxproj +0 -0
  71. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
  72. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -0
  73. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/xcuserdata/niklaspeterson.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  74. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/ios-message-stickers-template/stickers.xcodeproj/xcuserdata/niklaspeterson.xcuserdatad/xcschemes/xcschememanagement.plist +0 -0
  75. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/job.py +0 -0
  76. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/job_option.py +0 -0
  77. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/resources/NotoColorEmoji.ttf +0 -0
  78. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/resources/appicon.icns +0 -0
  79. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/resources/appicon.ico +0 -0
  80. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/resources/appicon.png +0 -0
  81. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/resources/compression.json +0 -0
  82. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/resources/emoji.json +0 -0
  83. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/resources/help.json +0 -0
  84. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/resources/input.json +0 -0
  85. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/resources/output.json +0 -0
  86. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/uploaders/__init__.py +0 -0
  87. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/uploaders/compress_wastickers.py +0 -0
  88. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/uploaders/upload_base.py +0 -0
  89. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/uploaders/upload_signal.py +0 -0
  90. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/uploaders/upload_telegram.py +0 -0
  91. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/uploaders/xcode_imessage.py +0 -0
  92. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/utils/auth/get_kakao_auth.py +0 -0
  93. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/utils/auth/get_line_auth.py +0 -0
  94. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/utils/auth/get_signal_auth.py +0 -0
  95. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/utils/callback.py +0 -0
  96. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/utils/files/cache_store.py +0 -0
  97. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/utils/files/json_manager.py +0 -0
  98. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/utils/files/json_resources_loader.py +0 -0
  99. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/utils/files/metadata_handler.py +0 -0
  100. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/utils/files/run_bin.py +0 -0
  101. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/utils/files/sanitize_filename.py +0 -0
  102. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/utils/media/apple_png_normalize.py +0 -0
  103. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/utils/media/codec_info.py +0 -0
  104. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/utils/media/decrypt_kakao.py +0 -0
  105. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/utils/media/format_verify.py +0 -0
  106. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert/utils/url_detect.py +0 -0
  107. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert.egg-info/SOURCES.txt +0 -0
  108. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert.egg-info/dependency_links.txt +0 -0
  109. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert.egg-info/entry_points.txt +0 -0
  110. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/src/sticker_convert.egg-info/top_level.txt +0 -0
  111. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/tests/test_compression.py +0 -0
  112. {sticker_convert-2.8.11 → sticker_convert-2.8.13}/tests/test_export.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sticker-convert
3
- Version: 2.8.11
3
+ Version: 2.8.13
4
4
  Summary: Convert (animated) stickers to/from WhatsApp, Telegram, Signal, Line, Kakao, Viber, iMessage. Written in Python.
5
5
  Author-email: laggykiller <chaudominic2@gmail.com>
6
6
  Maintainer-email: laggykiller <chaudominic2@gmail.com>
@@ -371,15 +371,16 @@ Requires-Dist: av~=12.1.0
371
371
  Requires-Dist: beautifulsoup4~=4.12.3
372
372
  Requires-Dist: rookiepy~=0.5.1
373
373
  Requires-Dist: imagequant~=1.1.1
374
+ Requires-Dist: js2py~=0.74
374
375
  Requires-Dist: memory-tempfile~=2.2.3
375
376
  Requires-Dist: mergedeep~=1.3.4
376
377
  Requires-Dist: numpy>=1.22.4
377
378
  Requires-Dist: Pillow~=10.3.0
378
379
  Requires-Dist: pyoxipng~=9.0.0
379
380
  Requires-Dist: python-telegram-bot~=21.2
380
- Requires-Dist: requests~=2.32.2
381
- Requires-Dist: rlottie_python~=1.3.4
382
- Requires-Dist: signalstickers-client-fork-laggykiller~=3.3.0.post1
381
+ Requires-Dist: requests~=2.32.3
382
+ Requires-Dist: rlottie_python~=1.3.5
383
+ Requires-Dist: signalstickers-client-fork-laggykiller~=3.3.0.post2
383
384
  Requires-Dist: sqlcipher3-wheels~=0.5.2.post1
384
385
  Requires-Dist: tqdm~=4.66.4
385
386
  Requires-Dist: ttkbootstrap-fork-laggykiller~=1.5.1
@@ -5,15 +5,16 @@ av~=12.1.0
5
5
  beautifulsoup4~=4.12.3
6
6
  rookiepy~=0.5.1
7
7
  imagequant~=1.1.1
8
+ js2py~=0.74
8
9
  memory-tempfile~=2.2.3
9
10
  mergedeep~=1.3.4
10
11
  numpy>=1.22.4
11
12
  Pillow~=10.3.0
12
13
  pyoxipng~=9.0.0
13
14
  python-telegram-bot~=21.2
14
- requests~=2.32.2
15
- rlottie_python~=1.3.4
16
- signalstickers-client-fork-laggykiller~=3.3.0.post1
15
+ requests~=2.32.3
16
+ rlottie_python~=1.3.5
17
+ signalstickers-client-fork-laggykiller~=3.3.0.post2
17
18
  sqlcipher3-wheels~=0.5.2.post1
18
19
  tqdm~=4.66.4
19
20
  ttkbootstrap-fork-laggykiller~=1.5.1
@@ -0,0 +1,406 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import itertools
5
+ import json
6
+ import zipfile
7
+ from io import BytesIO
8
+ from pathlib import Path
9
+ from typing import Any, List, Optional, Tuple, cast
10
+ from urllib.parse import urlparse
11
+
12
+ import js2py # type: ignore
13
+ import requests
14
+ from bs4 import BeautifulSoup
15
+ from bs4.element import Tag
16
+
17
+ from sticker_convert.downloaders.download_base import DownloadBase
18
+ from sticker_convert.job_option import CredOption
19
+ from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
20
+ from sticker_convert.utils.files.metadata_handler import MetadataHandler
21
+ from sticker_convert.utils.media.decrypt_kakao import DecryptKakao
22
+
23
+
24
+ def search_bracket(text: str, open_bracket: str = "{", close_bracket: str = "}") -> int:
25
+ depth = 0
26
+ is_str = False
27
+
28
+ for count, char in enumerate(text):
29
+ if char == '"':
30
+ is_str = not is_str
31
+
32
+ if is_str is False:
33
+ if char == open_bracket:
34
+ depth += 1
35
+ elif char == close_bracket:
36
+ depth -= 1
37
+
38
+ if depth == 0:
39
+ return count
40
+
41
+ return -1
42
+
43
+
44
+ class MetadataKakao:
45
+ @staticmethod
46
+ def get_info_from_share_link(url: str) -> Tuple[Optional[str], Optional[str]]:
47
+ headers = {"User-Agent": "Android"}
48
+
49
+ response = requests.get(url, headers=headers)
50
+ soup = BeautifulSoup(response.text, "html.parser")
51
+
52
+ pack_title_tag = soup.find("title") # type: ignore
53
+ if not pack_title_tag:
54
+ return None, None
55
+
56
+ pack_title: str = pack_title_tag.string # type: ignore
57
+
58
+ app_scheme_link_tag = soup.find("a", id="app_scheme_link") # type: ignore
59
+ assert isinstance(app_scheme_link_tag, Tag)
60
+
61
+ item_code_fake = cast(str, app_scheme_link_tag["data-i"])
62
+
63
+ js = ""
64
+ for script_tag in soup.find_all("script"):
65
+ js = script_tag.string
66
+ if js and "emoticonDeepLink" in js:
67
+ break
68
+ if "emoticonDeepLink" not in js:
69
+ return None, None
70
+
71
+ func_start_pos = js.find("function emoticonDeepLink(")
72
+ js = js[func_start_pos:]
73
+ bracket_start_pos = js.find("{")
74
+ func_end_pos = search_bracket(js[bracket_start_pos:]) + bracket_start_pos
75
+ js = js[: func_end_pos + 1]
76
+ web2app_start_pos = js.find("daumtools.web2app(")
77
+ js = js[:web2app_start_pos] + "return a;}"
78
+ get_item_code = js2py.eval_js(js) # type: ignore
79
+ kakao_scheme_link = cast(
80
+ str,
81
+ get_item_code(
82
+ "kakaotalk://store/emoticon/${i}?referer=share_link", item_code_fake
83
+ ),
84
+ )
85
+ item_code = urlparse(kakao_scheme_link).path.split("/")[-1]
86
+
87
+ return pack_title, item_code
88
+
89
+ @staticmethod
90
+ def get_item_code(title_ko: str, auth_token: str) -> Optional[str]:
91
+ headers = {
92
+ "Authorization": auth_token,
93
+ }
94
+
95
+ data = {"query": title_ko}
96
+
97
+ response = requests.post(
98
+ "https://talk-pilsner.kakao.com/emoticon/item_store/instant_search",
99
+ headers=headers,
100
+ data=data,
101
+ )
102
+
103
+ if response.status_code != 200:
104
+ return None
105
+
106
+ response_json = json.loads(response.text)
107
+ item_code = response_json["emoticons"][0]["item_code"]
108
+
109
+ return item_code
110
+
111
+ @staticmethod
112
+ def get_pack_info_unauthed(
113
+ pack_title: str,
114
+ ) -> Optional[dict[str, Any]]:
115
+ pack_meta_r = requests.get(f"https://e.kakao.com/api/v1/items/t/{pack_title}")
116
+
117
+ if pack_meta_r.status_code == 200:
118
+ pack_meta = json.loads(pack_meta_r.text)
119
+ else:
120
+ return None
121
+
122
+ return pack_meta
123
+
124
+ @staticmethod
125
+ def get_pack_info_authed(
126
+ item_code: str, auth_token: str
127
+ ) -> Optional[dict[str, Any]]:
128
+ headers = {
129
+ "Authorization": auth_token,
130
+ "Talk-Agent": "android/10.8.1",
131
+ "Talk-Language": "en",
132
+ "User-Agent": "okhttp/4.10.0",
133
+ }
134
+
135
+ response = requests.post(
136
+ f"https://talk-pilsner.kakao.com/emoticon/api/store/v3/items/{item_code}",
137
+ headers=headers,
138
+ )
139
+
140
+ if response.status_code != 200:
141
+ return None
142
+
143
+ response_json = json.loads(response.text)
144
+
145
+ return response_json
146
+
147
+
148
+ class DownloadKakao(DownloadBase):
149
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
150
+ super().__init__(*args, **kwargs)
151
+ self.pack_title: Optional[str] = None
152
+ self.author: Optional[str] = None
153
+ self.auth_token: Optional[str] = None
154
+
155
+ self.pack_info_unauthed: Optional[dict[str, Any]] = None
156
+ self.pack_info_authed: Optional[dict[str, Any]] = None
157
+
158
+ def download_stickers_kakao(self) -> bool:
159
+ self.auth_token = None
160
+ if self.opt_cred:
161
+ self.auth_token = self.opt_cred.kakao_auth_token
162
+
163
+ if urlparse(self.url).netloc == "emoticon.kakao.com":
164
+ self.pack_title, item_code = MetadataKakao.get_info_from_share_link(
165
+ self.url
166
+ )
167
+
168
+ if item_code:
169
+ return self.download_animated(item_code)
170
+ self.cb.put("Download failed: Cannot download metadata for sticker pack")
171
+ return False
172
+
173
+ if self.url.isnumeric() or self.url.startswith("kakaotalk://store/emoticon/"):
174
+ item_code = self.url.replace("kakaotalk://store/emoticon/", "")
175
+
176
+ self.pack_title = None
177
+ if self.auth_token:
178
+ self.pack_info_authed = MetadataKakao.get_pack_info_authed(
179
+ item_code, self.auth_token
180
+ )
181
+ if self.pack_info_authed:
182
+ self.pack_title = self.pack_info_authed["itemUnitInfo"][0]["title"]
183
+ else:
184
+ self.cb.put("Warning: Cannot get pack_title with auth_token.")
185
+ self.cb.put(
186
+ "Is auth_token invalid / expired? Try to regenerate it."
187
+ )
188
+ self.cb.put("Continuing without getting pack_title")
189
+
190
+ return self.download_animated(item_code)
191
+
192
+ if urlparse(self.url).netloc == "e.kakao.com":
193
+ self.pack_title = urlparse(self.url).path.split("/")[-1]
194
+ self.pack_info_unauthed = MetadataKakao.get_pack_info_unauthed(
195
+ self.pack_title
196
+ )
197
+
198
+ if not self.pack_info_unauthed:
199
+ self.cb.put(
200
+ "Download failed: Cannot download metadata for sticker pack"
201
+ )
202
+ return False
203
+
204
+ self.author = self.pack_info_unauthed["result"]["artist"]
205
+ title_ko = self.pack_info_unauthed["result"]["title"]
206
+ thumbnail_urls = self.pack_info_unauthed["result"]["thumbnailUrls"]
207
+
208
+ if self.auth_token:
209
+ item_code = MetadataKakao.get_item_code(title_ko, self.auth_token)
210
+ if item_code:
211
+ return self.download_animated(item_code)
212
+ msg = "Warning: Cannot get item code.\n"
213
+ msg += "Is auth_token invalid / expired? Try to regenerate it.\n"
214
+ msg += "Continue to download static stickers instead?"
215
+ self.cb.put(("ask_bool", (msg,), None))
216
+ if self.cb_return:
217
+ response = self.cb_return.get_response()
218
+ else:
219
+ response = False
220
+
221
+ if response is False:
222
+ return False
223
+
224
+ return self.download_static(thumbnail_urls)
225
+
226
+ self.cb.put("Download failed: Unrecognized URL")
227
+ return False
228
+
229
+ def download_static(self, thumbnail_urls: str) -> bool:
230
+ MetadataHandler.set_metadata(
231
+ self.out_dir, title=self.pack_title, author=self.author
232
+ )
233
+
234
+ targets: List[Tuple[str, Path]] = []
235
+
236
+ for num, url in enumerate(thumbnail_urls):
237
+ dest = Path(self.out_dir, str(num).zfill(3) + ".png")
238
+ targets.append((url, dest))
239
+
240
+ self.download_multiple_files(targets)
241
+
242
+ return True
243
+
244
+ def download_animated(self, item_code: str) -> bool:
245
+ MetadataHandler.set_metadata(
246
+ self.out_dir, title=self.pack_title, author=self.author
247
+ )
248
+
249
+ success = self.download_animated_zip(item_code)
250
+ if not success:
251
+ self.cb.put("Trying to download one by one")
252
+ success = self.download_animated_files(item_code)
253
+
254
+ return success
255
+
256
+ def download_animated_files(self, item_code: str) -> bool:
257
+ play_exts = [".webp", ".gif", ".png", ""]
258
+ play_types = ["emot", "emoji", ""] # emot = normal; emoji = mini
259
+ play_path_format = None
260
+ sound_exts = [".mp3", ""]
261
+ sound_path_format = None
262
+ stickers_count = 32 # https://emoticonstudio.kakao.com/pages/start
263
+
264
+ if not self.pack_info_authed and self.auth_token:
265
+ self.pack_info_authed = MetadataKakao.get_pack_info_authed(
266
+ item_code, self.auth_token
267
+ )
268
+ if self.pack_info_authed:
269
+ preview_data = self.pack_info_authed["itemUnitInfo"][0]["previewData"]
270
+ play_path_format = preview_data["playPathFormat"]
271
+ sound_path_format = preview_data["soundPathFormat"]
272
+ stickers_count = preview_data["num"]
273
+ else:
274
+ if not self.pack_info_unauthed:
275
+ public_url = None
276
+ if urlparse(self.url).netloc == "emoticon.kakao.com":
277
+ r = requests.get(self.url)
278
+ # Share url would redirect to public url without headers
279
+ public_url = r.url
280
+ elif urlparse(self.url).netloc == "e.kakao.com":
281
+ public_url = self.url
282
+ if public_url:
283
+ pack_title = urlparse(public_url).path.split("/")[-1]
284
+ self.pack_info_unauthed = MetadataKakao.get_pack_info_unauthed(
285
+ pack_title
286
+ )
287
+
288
+ if self.pack_info_unauthed:
289
+ stickers_count = len(self.pack_info_unauthed["result"]["thumbnailUrls"])
290
+
291
+ play_type = ""
292
+ play_ext = ""
293
+ if play_path_format is None:
294
+ for play_type, play_ext in itertools.product(play_types, play_exts):
295
+ r = requests.get(
296
+ f"https://item.kakaocdn.net/dw/{item_code}.{play_type}_001{play_ext}"
297
+ )
298
+ if r.ok:
299
+ break
300
+ if play_ext == "":
301
+ self.cb.put(f"Failed to determine extension of {item_code}")
302
+ return False
303
+ else:
304
+ play_path_format = f"dw/{item_code}.{play_type}_0##{play_ext}"
305
+ else:
306
+ play_ext = "." + play_path_format.split(".")[-1]
307
+
308
+ sound_ext = ""
309
+ if sound_path_format is None:
310
+ for sound_ext in sound_exts:
311
+ r = requests.get(
312
+ f"https://item.kakaocdn.net/dw/{item_code}.sound_001{sound_ext}"
313
+ )
314
+ if r.ok:
315
+ break
316
+ if sound_ext != "":
317
+ sound_path_format = f"dw/{item_code}.sound_0##{sound_ext}"
318
+ elif sound_path_format != "":
319
+ sound_ext = "." + sound_path_format.split(".")[-1]
320
+
321
+ assert play_path_format
322
+ targets: list[tuple[str, Path]] = []
323
+ for num in range(1, stickers_count + 1):
324
+ play_url = "https://item.kakaocdn.net/" + play_path_format.replace(
325
+ "##", str(num).zfill(2)
326
+ )
327
+ play_dl_path = Path(self.out_dir, str(num).zfill(3) + play_ext)
328
+ targets.append((play_url, play_dl_path))
329
+
330
+ if sound_path_format:
331
+ sound_url = "https://item.kakaocdn.net/" + sound_path_format.replace(
332
+ "##", str(num).zfill(2)
333
+ )
334
+ sound_dl_path = Path(self.out_dir, str(num).zfill(3) + sound_ext)
335
+ targets.append((sound_url, sound_dl_path))
336
+
337
+ self.download_multiple_files(targets)
338
+
339
+ for target in targets:
340
+ f_path = target[1]
341
+ ext = Path(f_path).suffix
342
+
343
+ if ext not in (".gif", ".webp"):
344
+ continue
345
+
346
+ with open(f_path, "rb") as f:
347
+ data = f.read()
348
+ data = DecryptKakao.xor_data(data)
349
+ self.cb.put(f"Decrypted {f_path}")
350
+ with open(f_path, "wb+") as f:
351
+ f.write(data)
352
+
353
+ self.cb.put(f"Finished getting {item_code}")
354
+
355
+ return True
356
+
357
+ def download_animated_zip(self, item_code: str) -> bool:
358
+ pack_url = f"http://item.kakaocdn.net/dw/{item_code}.file_pack.zip"
359
+
360
+ zip_file = self.download_file(pack_url)
361
+ if zip_file:
362
+ self.cb.put(f"Downloaded {pack_url}")
363
+ else:
364
+ self.cb.put(f"Cannot download {pack_url}")
365
+ return False
366
+
367
+ with zipfile.ZipFile(BytesIO(zip_file)) as zf:
368
+ self.cb.put("Unzipping...")
369
+ self.cb.put(
370
+ (
371
+ "bar",
372
+ None,
373
+ {"set_progress_mode": "determinate", "steps": len(zf.namelist())},
374
+ )
375
+ )
376
+
377
+ for num, f_path in enumerate(sorted(zf.namelist())):
378
+ ext = Path(f_path).suffix
379
+
380
+ if ext in (".gif", ".webp"):
381
+ data = DecryptKakao.xor_data(zf.read(f_path))
382
+ self.cb.put(f"Decrypted {f_path}")
383
+ else:
384
+ data = zf.read(f_path)
385
+ self.cb.put(f"Read {f_path}")
386
+
387
+ out_path = Path(self.out_dir, str(num).zfill(3) + ext)
388
+ with open(out_path, "wb") as f:
389
+ f.write(data)
390
+
391
+ self.cb.put("update_bar")
392
+
393
+ self.cb.put(f"Finished getting {pack_url}")
394
+
395
+ return True
396
+
397
+ @staticmethod
398
+ def start(
399
+ url: str,
400
+ out_dir: Path,
401
+ opt_cred: Optional[CredOption],
402
+ cb: CallbackProtocol,
403
+ cb_return: CallbackReturn,
404
+ ) -> bool:
405
+ downloader = DownloadKakao(url, out_dir, opt_cred, cb, cb_return)
406
+ return downloader.download_stickers_kakao()
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
- __version__ = "2.8.11"
3
+ __version__ = "2.8.13"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sticker-convert
3
- Version: 2.8.11
3
+ Version: 2.8.13
4
4
  Summary: Convert (animated) stickers to/from WhatsApp, Telegram, Signal, Line, Kakao, Viber, iMessage. Written in Python.
5
5
  Author-email: laggykiller <chaudominic2@gmail.com>
6
6
  Maintainer-email: laggykiller <chaudominic2@gmail.com>
@@ -371,15 +371,16 @@ Requires-Dist: av~=12.1.0
371
371
  Requires-Dist: beautifulsoup4~=4.12.3
372
372
  Requires-Dist: rookiepy~=0.5.1
373
373
  Requires-Dist: imagequant~=1.1.1
374
+ Requires-Dist: js2py~=0.74
374
375
  Requires-Dist: memory-tempfile~=2.2.3
375
376
  Requires-Dist: mergedeep~=1.3.4
376
377
  Requires-Dist: numpy>=1.22.4
377
378
  Requires-Dist: Pillow~=10.3.0
378
379
  Requires-Dist: pyoxipng~=9.0.0
379
380
  Requires-Dist: python-telegram-bot~=21.2
380
- Requires-Dist: requests~=2.32.2
381
- Requires-Dist: rlottie_python~=1.3.4
382
- Requires-Dist: signalstickers-client-fork-laggykiller~=3.3.0.post1
381
+ Requires-Dist: requests~=2.32.3
382
+ Requires-Dist: rlottie_python~=1.3.5
383
+ Requires-Dist: signalstickers-client-fork-laggykiller~=3.3.0.post2
383
384
  Requires-Dist: sqlcipher3-wheels~=0.5.2.post1
384
385
  Requires-Dist: tqdm~=4.66.4
385
386
  Requires-Dist: ttkbootstrap-fork-laggykiller~=1.5.1
@@ -5,15 +5,16 @@ av~=12.1.0
5
5
  beautifulsoup4~=4.12.3
6
6
  rookiepy~=0.5.1
7
7
  imagequant~=1.1.1
8
+ js2py~=0.74
8
9
  memory-tempfile~=2.2.3
9
10
  mergedeep~=1.3.4
10
11
  numpy>=1.22.4
11
12
  Pillow~=10.3.0
12
13
  pyoxipng~=9.0.0
13
14
  python-telegram-bot~=21.2
14
- requests~=2.32.2
15
- rlottie_python~=1.3.4
16
- signalstickers-client-fork-laggykiller~=3.3.0.post1
15
+ requests~=2.32.3
16
+ rlottie_python~=1.3.5
17
+ signalstickers-client-fork-laggykiller~=3.3.0.post2
17
18
  sqlcipher3-wheels~=0.5.2.post1
18
19
  tqdm~=4.66.4
19
20
  ttkbootstrap-fork-laggykiller~=1.5.1
@@ -414,6 +414,20 @@ def test_download_kakao_animated_gif_share_link(tmp_path: LocalPath) -> None:
414
414
  )
415
415
 
416
416
 
417
+ @pytest.mark.skipif(not TEST_DOWNLOAD, reason="TEST_DOWNLOAD not set")
418
+ def test_download_kakao_mini_share_link(tmp_path: LocalPath) -> None:
419
+ _run_sticker_convert(
420
+ tmp_path=tmp_path,
421
+ source="kakao",
422
+ url="https://emoticon.kakao.com/items/EL7hYM1hlS0X-9LuBH_9fOc3xNg=?lang=ko&referer=share_link",
423
+ expected_file_count=35,
424
+ expected_file_formats=[".webp"],
425
+ with_title=True,
426
+ with_author=True,
427
+ with_emoji=False,
428
+ )
429
+
430
+
417
431
  @pytest.mark.skipif(not TEST_DOWNLOAD, reason="TEST_DOWNLOAD not set")
418
432
  def test_download_viber_custom_sticker_packs(tmp_path: LocalPath) -> None:
419
433
  _run_sticker_convert(
@@ -1,255 +0,0 @@
1
- #!/usr/bin/env python3
2
- from __future__ import annotations
3
-
4
- import json
5
- import zipfile
6
- from io import BytesIO
7
- from pathlib import Path
8
- from typing import Any, List, Optional, Tuple
9
- from urllib.parse import urlparse
10
-
11
- import requests
12
- from bs4 import BeautifulSoup
13
- from bs4.element import Tag
14
-
15
- from sticker_convert.downloaders.download_base import DownloadBase
16
- from sticker_convert.job_option import CredOption
17
- from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
18
- from sticker_convert.utils.files.metadata_handler import MetadataHandler
19
- from sticker_convert.utils.media.decrypt_kakao import DecryptKakao
20
-
21
-
22
- class MetadataKakao:
23
- @staticmethod
24
- def get_info_from_share_link(url: str) -> Tuple[Optional[str], Optional[str]]:
25
- headers = {"User-Agent": "Android"}
26
-
27
- response = requests.get(url, headers=headers)
28
- soup = BeautifulSoup(response.text, "html.parser")
29
-
30
- pack_title_tag = soup.find("title") # type: ignore
31
- if not pack_title_tag:
32
- return None, None
33
-
34
- pack_title: str = pack_title_tag.string # type: ignore
35
-
36
- app_scheme_link_tag = soup.find("a", id="app_scheme_link") # type: ignore
37
- assert isinstance(app_scheme_link_tag, Tag)
38
-
39
- data_urls = app_scheme_link_tag.get("data-url")
40
- if not data_urls:
41
- return None, None
42
- if isinstance(data_urls, list):
43
- data_url = data_urls[0]
44
- else:
45
- data_url = data_urls
46
-
47
- item_code = data_url.replace("kakaotalk://store/emoticon/", "").split("?")[0]
48
-
49
- return pack_title, item_code
50
-
51
- @staticmethod
52
- def get_info_from_pack_title(
53
- pack_title: str,
54
- ) -> Tuple[Optional[str], Optional[str], Optional[str]]:
55
- pack_meta_r = requests.get(f"https://e.kakao.com/api/v1/items/t/{pack_title}")
56
-
57
- if pack_meta_r.status_code == 200:
58
- pack_meta = json.loads(pack_meta_r.text)
59
- else:
60
- return None, None, None
61
-
62
- author = pack_meta["result"]["artist"]
63
- title_ko = pack_meta["result"]["title"]
64
- thumbnail_urls = pack_meta["result"]["thumbnailUrls"]
65
-
66
- return author, title_ko, thumbnail_urls
67
-
68
- @staticmethod
69
- def get_item_code(title_ko: str, auth_token: str) -> Optional[str]:
70
- headers = {
71
- "Authorization": auth_token,
72
- }
73
-
74
- data = {"query": title_ko}
75
-
76
- response = requests.post(
77
- "https://talk-pilsner.kakao.com/emoticon/item_store/instant_search",
78
- headers=headers,
79
- data=data,
80
- )
81
-
82
- if response.status_code != 200:
83
- return None
84
-
85
- response_json = json.loads(response.text)
86
- item_code = response_json["emoticons"][0]["item_code"]
87
-
88
- return item_code
89
-
90
- @staticmethod
91
- def get_title_from_id(item_code: str, auth_token: str) -> Optional[str]:
92
- headers = {
93
- "Authorization": auth_token,
94
- }
95
-
96
- response = requests.post(
97
- f"https://talk-pilsner.kakao.com/emoticon/api/store/v3/items/{item_code}",
98
- headers=headers,
99
- )
100
-
101
- if response.status_code != 200:
102
- return None
103
-
104
- response_json = json.loads(response.text)
105
- title = response_json["itemUnitInfo"][0]["title"]
106
- # play_path_format = response_json['itemUnitInfo'][0]['playPathFormat']
107
- # stickers_count = len(response_json['itemUnitInfo'][0]['sizes'])
108
-
109
- return title
110
-
111
-
112
- class DownloadKakao(DownloadBase):
113
- def __init__(self, *args: Any, **kwargs: Any) -> None:
114
- super().__init__(*args, **kwargs)
115
- self.pack_title: Optional[str] = None
116
- self.author: Optional[str] = None
117
-
118
- def download_stickers_kakao(self) -> bool:
119
- auth_token = None
120
- if self.opt_cred:
121
- auth_token = self.opt_cred.kakao_auth_token
122
-
123
- if urlparse(self.url).netloc == "emoticon.kakao.com":
124
- self.pack_title, item_code = MetadataKakao.get_info_from_share_link(
125
- self.url
126
- )
127
-
128
- if item_code:
129
- return self.download_animated(item_code)
130
- self.cb.put("Download failed: Cannot download metadata for sticker pack")
131
- return False
132
-
133
- if self.url.isnumeric() or self.url.startswith("kakaotalk://store/emoticon/"):
134
- item_code = self.url.replace("kakaotalk://store/emoticon/", "")
135
-
136
- self.pack_title = None
137
- if auth_token:
138
- self.pack_title = MetadataKakao.get_title_from_id(item_code, auth_token)
139
- if not self.pack_title:
140
- self.cb.put("Warning: Cannot get pack_title with auth_token.")
141
- self.cb.put(
142
- "Is auth_token invalid / expired? Try to regenerate it."
143
- )
144
- self.cb.put("Continuing without getting pack_title")
145
-
146
- return self.download_animated(item_code)
147
-
148
- if urlparse(self.url).netloc == "e.kakao.com":
149
- self.pack_title = self.url.replace("https://e.kakao.com/t/", "")
150
- (
151
- self.author,
152
- title_ko,
153
- thumbnail_urls,
154
- ) = MetadataKakao.get_info_from_pack_title(self.pack_title)
155
-
156
- assert self.author
157
- assert title_ko
158
- assert thumbnail_urls
159
-
160
- if not thumbnail_urls:
161
- self.cb.put(
162
- "Download failed: Cannot download metadata for sticker pack"
163
- )
164
- return False
165
-
166
- if auth_token:
167
- item_code = MetadataKakao.get_item_code(title_ko, auth_token)
168
- if item_code:
169
- return self.download_animated(item_code)
170
- msg = "Warning: Cannot get item code.\n"
171
- msg += "Is auth_token invalid / expired? Try to regenerate it.\n"
172
- msg += "Continue to download static stickers instead?"
173
- self.cb.put(("ask_bool", (msg,), None))
174
- if self.cb_return:
175
- response = self.cb_return.get_response()
176
- else:
177
- response = False
178
-
179
- if response is False:
180
- return False
181
-
182
- return self.download_static(thumbnail_urls)
183
-
184
- self.cb.put("Download failed: Unrecognized URL")
185
- return False
186
-
187
- def download_static(self, thumbnail_urls: str) -> bool:
188
- MetadataHandler.set_metadata(
189
- self.out_dir, title=self.pack_title, author=self.author
190
- )
191
-
192
- targets: List[Tuple[str, Path]] = []
193
-
194
- for num, url in enumerate(thumbnail_urls):
195
- dest = Path(self.out_dir, str(num).zfill(3) + ".png")
196
- targets.append((url, dest))
197
-
198
- self.download_multiple_files(targets)
199
-
200
- return True
201
-
202
- def download_animated(self, item_code: str) -> bool:
203
- MetadataHandler.set_metadata(
204
- self.out_dir, title=self.pack_title, author=self.author
205
- )
206
-
207
- pack_url = f"http://item.kakaocdn.net/dw/{item_code}.file_pack.zip"
208
-
209
- zip_file = self.download_file(pack_url)
210
- if zip_file:
211
- self.cb.put(f"Downloaded {pack_url}")
212
- else:
213
- self.cb.put(f"Cannot download {pack_url}")
214
- return False
215
-
216
- with zipfile.ZipFile(BytesIO(zip_file)) as zf:
217
- self.cb.put("Unzipping...")
218
- self.cb.put(
219
- (
220
- "bar",
221
- None,
222
- {"set_progress_mode": "determinate", "steps": len(zf.namelist())},
223
- )
224
- )
225
-
226
- for num, f_path in enumerate(sorted(zf.namelist())):
227
- ext = Path(f_path).suffix
228
-
229
- if ext in (".gif", ".webp"):
230
- data = DecryptKakao.xor_data(zf.read(f_path))
231
- self.cb.put(f"Decrypted {f_path}")
232
- else:
233
- data = zf.read(f_path)
234
- self.cb.put(f"Read {f_path}")
235
-
236
- out_path = Path(self.out_dir, str(num).zfill(3) + ext)
237
- with open(out_path, "wb") as f:
238
- f.write(data)
239
-
240
- self.cb.put("update_bar")
241
-
242
- self.cb.put(f"Finished getting {pack_url}")
243
-
244
- return True
245
-
246
- @staticmethod
247
- def start(
248
- url: str,
249
- out_dir: Path,
250
- opt_cred: Optional[CredOption],
251
- cb: CallbackProtocol,
252
- cb_return: CallbackReturn,
253
- ) -> bool:
254
- downloader = DownloadKakao(url, out_dir, opt_cred, cb, cb_return)
255
- return downloader.download_stickers_kakao()