sticker-convert 2.11.8__py3-none-any.whl → 2.12.0__py3-none-any.whl

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 (55) hide show
  1. sticker_convert/converter.py +72 -0
  2. sticker_convert/definitions.py +7 -0
  3. sticker_convert/downloaders/download_kakao.py +63 -78
  4. sticker_convert/downloaders/download_viber.py +20 -6
  5. sticker_convert/ios-message-stickers-template/.github/FUNDING.yml +0 -0
  6. sticker_convert/ios-message-stickers-template/.gitignore +0 -0
  7. sticker_convert/ios-message-stickers-template/README.md +0 -0
  8. sticker_convert/ios-message-stickers-template/stickers/Info.plist +0 -0
  9. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Info.plist +0 -0
  10. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Contents.json +0 -0
  11. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Contents.json +0 -0
  12. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 1.sticker/Contents.json +0 -0
  13. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 1.sticker/Sticker 1.png +0 -0
  14. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 2.sticker/Contents.json +0 -0
  15. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 2.sticker/Sticker 2.png +0 -0
  16. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 3.sticker/Contents.json +0 -0
  17. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 3.sticker/Sticker 3.png +0 -0
  18. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/App-Store-1024x1024pt.png +0 -0
  19. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Contents.json +0 -0
  20. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-App-Store-1024x768pt.png +0 -0
  21. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPad-67x50pt@2x.png +0 -0
  22. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPad-Pro-74x55pt@2x.png +0 -0
  23. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPhone-60x45pt@2x.png +0 -0
  24. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPhone-60x45pt@3x.png +0 -0
  25. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages27x20pt@2x.png +0 -0
  26. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages27x20pt@3x.png +0 -0
  27. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages32x24pt@2x.png +0 -0
  28. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages32x24pt@3x.png +0 -0
  29. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPad-Settings-29pt@2x.png +0 -0
  30. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPhone-Settings-29pt@3x.png +0 -0
  31. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPhone-settings-29pt@2x.png +0 -0
  32. sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.pbxproj +0 -0
  33. sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
  34. sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -0
  35. sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/xcuserdata/niklaspeterson.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  36. sticker_convert/ios-message-stickers-template/stickers.xcodeproj/xcuserdata/niklaspeterson.xcuserdatad/xcschemes/xcschememanagement.plist +0 -0
  37. sticker_convert/resources/emoji.json +0 -0
  38. sticker_convert/resources/help.json +0 -0
  39. sticker_convert/resources/input.json +1 -1
  40. sticker_convert/resources/memdump_windows.ps1 +0 -0
  41. sticker_convert/resources/output.json +0 -0
  42. sticker_convert/uploaders/upload_viber.py +13 -14
  43. sticker_convert/utils/auth/get_kakao_auth.py +34 -4
  44. sticker_convert/utils/auth/get_kakao_desktop_auth.py +1 -1
  45. sticker_convert/utils/chrome_remotedebug.py +78 -15
  46. sticker_convert/utils/files/metadata_handler.py +1 -0
  47. sticker_convert/utils/media/codec_info.py +53 -6
  48. sticker_convert/utils/url_detect.py +1 -3
  49. sticker_convert/version.py +1 -1
  50. {sticker_convert-2.11.8.dist-info → sticker_convert-2.12.0.dist-info}/METADATA +4 -5
  51. {sticker_convert-2.11.8.dist-info → sticker_convert-2.12.0.dist-info}/RECORD +19 -19
  52. {sticker_convert-2.11.8.dist-info → sticker_convert-2.12.0.dist-info}/WHEEL +0 -0
  53. {sticker_convert-2.11.8.dist-info → sticker_convert-2.12.0.dist-info}/entry_points.txt +0 -0
  54. {sticker_convert-2.11.8.dist-info → sticker_convert-2.12.0.dist-info}/licenses/LICENSE +0 -0
  55. {sticker_convert-2.11.8.dist-info → sticker_convert-2.12.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env python3
2
+ import json
2
3
  import os
3
4
  from fractions import Fraction
4
5
  from io import BytesIO
@@ -7,12 +8,14 @@ from pathlib import Path
7
8
  from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Union, cast
8
9
 
9
10
  import numpy as np
11
+ from bs4 import BeautifulSoup
10
12
  from PIL import Image
11
13
  from PIL import __version__ as PillowVersion
12
14
  from PIL import features
13
15
 
14
16
  from sticker_convert.job_option import CompOption
15
17
  from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
18
+ from sticker_convert.utils.chrome_remotedebug import CRD
16
19
  from sticker_convert.utils.files.cache_store import CacheStore
17
20
  from sticker_convert.utils.media.codec_info import CodecInfo, rounding
18
21
  from sticker_convert.utils.media.format_verify import FormatVerify
@@ -423,9 +426,78 @@ class StickerConvert:
423
426
  # ffmpeg do not support webp decoding (yet)
424
427
  # ffmpeg could fail to decode apng if file is buggy
425
428
  self._frames_import_pillow()
429
+ elif suffix == ".svg":
430
+ self._frames_import_svg()
426
431
  else:
427
432
  self._frames_import_pyav()
428
433
 
434
+ def _frames_import_svg(self) -> None:
435
+ width = self.codec_info_orig.res[0]
436
+ height = self.codec_info_orig.res[1]
437
+
438
+ chrome_path = CRD.get_chrome_path()
439
+ args = [
440
+ "--headless",
441
+ "--disable-extensions",
442
+ "--disable-infobars",
443
+ "--disable-gpu",
444
+ "--disable-gpu-rasterization",
445
+ "--hide-scrollbars",
446
+ f"--window-size={width + 100},{height + 100}",
447
+ "about:blank",
448
+ ]
449
+ if chrome_path is None:
450
+ raise RuntimeError("[F] Chrome/Chromium required for importing svg")
451
+ self.cb.put("[W] Importing SVG takes long time")
452
+
453
+ if isinstance(self.in_f, bytes):
454
+ svg = self.in_f.decode()
455
+ else:
456
+ with open(self.in_f) as f:
457
+ svg = f.read()
458
+ soup = BeautifulSoup(svg, "html.parser")
459
+ svg_tag = soup.find_all("svg")[0]
460
+
461
+ if svg_tag.get("width") is None:
462
+ svg_tag["width"] = width
463
+ if svg_tag.get("height") is None:
464
+ svg_tag["height"] = height
465
+ svg = str(soup)
466
+
467
+ crd = None
468
+ try:
469
+ crd = CRD(chrome_path, args=args)
470
+ crd.connect(-1)
471
+ crd.open_html_str(svg)
472
+ crd.set_transparent_bg()
473
+ crd.exec_js('svg = document.getElementsByTagName("svg")[0]')
474
+ x = json.loads(crd.exec_js("svg.getBoundingClientRect().x"))["result"][
475
+ "result"
476
+ ]["value"]
477
+ y = json.loads(crd.exec_js("svg.getBoundingClientRect().y"))["result"][
478
+ "result"
479
+ ]["value"]
480
+ clip = {"x": x, "y": y, "width": width, "height": height, "scale": 1}
481
+
482
+ if self.codec_info_orig.fps > 0:
483
+ crd.exec_js("svg.pauseAnimations()")
484
+ for i in range(self.codec_info_orig.frames):
485
+ curr_time = (
486
+ i
487
+ / self.codec_info_orig.frames
488
+ * self.codec_info_orig.duration
489
+ / 1000
490
+ )
491
+ crd.exec_js(f"svg.setCurrentTime({curr_time})")
492
+ self.frames_raw.append(
493
+ np.asarray(crd.screenshot(clip).convert("RGBA"))
494
+ )
495
+ else:
496
+ self.frames_raw.append(np.asarray(crd.screenshot(clip).convert("RGBA")))
497
+ finally:
498
+ if crd is not None:
499
+ crd.close()
500
+
429
501
  def _frames_import_pillow(self) -> None:
430
502
  with Image.open(self.in_f) as im:
431
503
  # Note: im.convert("RGBA") would return rgba image of current frame only
@@ -82,3 +82,10 @@ def get_config_dir() -> Path:
82
82
 
83
83
  # Directory for saving configs
84
84
  CONFIG_DIR = get_config_dir()
85
+
86
+ # When importing SVG, import at this fps
87
+ SVG_SAMPLE_FPS = 30
88
+
89
+ # If width and height not set in SVG tag, import at this dimension
90
+ SVG_DEFAULT_WIDTH = 1024
91
+ SVG_DEFAULT_HEIGHT = 1024
@@ -4,12 +4,10 @@ from __future__ import annotations
4
4
  import itertools
5
5
  import json
6
6
  from pathlib import Path
7
- from typing import Any, List, Optional, Tuple, cast
7
+ from typing import Any, List, Optional, Tuple
8
8
  from urllib.parse import urlparse
9
9
 
10
10
  import requests
11
- from bs4 import BeautifulSoup
12
- from py_mini_racer import MiniRacer
13
11
 
14
12
  from sticker_convert.downloaders.download_base import DownloadBase
15
13
  from sticker_convert.job_option import CredOption, InputOption
@@ -17,34 +15,32 @@ from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
17
15
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
18
16
  from sticker_convert.utils.media.decrypt_kakao import DecryptKakao
19
17
 
20
- JSINJECT = """
21
- class osclass {
22
- android = true;
23
- }
24
- class uaclass {
25
- os = new osclass();
26
- }
27
- class util {
28
- static userAgent() {
29
- return new uaclass();
30
- }
31
- }
32
- class daumtools {
33
- static web2app(dataDict) {
34
- return dataDict['urlScheme'];
35
- }
36
- }
37
- class document {
38
- static querySelectorAll(selectors) {
39
- return [];
40
- }
41
- }
42
- """
43
-
44
18
 
45
19
  class MetadataKakao:
46
20
  @staticmethod
47
- def get_item_code(title_ko: str, auth_token: str) -> Optional[str]:
21
+ def get_item_code_from_hash(hash: str, auth_token: str) -> Optional[str]:
22
+ headers = {
23
+ "Authorization": auth_token,
24
+ }
25
+
26
+ data = {"hashedItemCode": hash}
27
+
28
+ response = requests.post(
29
+ "https://talk-pilsner.kakao.com/emoticon/api/store/v3/item-code-by-hash",
30
+ headers=headers,
31
+ data=data,
32
+ )
33
+
34
+ if response.status_code != 200:
35
+ return None
36
+
37
+ response_json = json.loads(response.text)
38
+ item_code = response_json["itemCode"]
39
+
40
+ return item_code
41
+
42
+ @staticmethod
43
+ def get_item_code_from_title(title_ko: str, auth_token: str) -> Optional[str]:
48
44
  headers = {
49
45
  "Authorization": auth_token,
50
46
  }
@@ -112,66 +108,53 @@ class DownloadKakao(DownloadBase):
112
108
  self.pack_info_unauthed: Optional[dict[str, Any]] = None
113
109
  self.pack_info_authed: Optional[dict[str, Any]] = None
114
110
 
115
- def get_info_from_share_link(self, url: str) -> Tuple[Optional[str], Optional[str]]:
116
- headers = {"User-Agent": "Android"}
117
-
118
- response = requests.get(url, headers=headers)
119
- soup = BeautifulSoup(response.content.decode("utf-8", "ignore"), "html.parser")
120
-
121
- pack_title_tag = soup.find("title") # type: ignore
122
- if not pack_title_tag:
123
- return None, None
124
-
125
- pack_title: str = pack_title_tag.string # type: ignore
126
-
127
- js = ""
128
- for script_tag in soup.find_all("script"):
129
- js = script_tag.string
130
- if js and "daumtools.web2app" in js:
131
- break
132
- if "daumtools.web2app" not in js:
133
- return None, None
134
-
135
- js = JSINJECT + js
136
-
137
- ctx = MiniRacer()
138
- kakao_url = cast(str, ctx.eval(js))
139
- item_code = urlparse(kakao_url).path.split("/")[2]
140
-
141
- # Share link redirect to preview link if use desktop headers
142
- # This allows us to find pack author
143
- headers_desktop = {"User-Agent": "Chrome"}
144
-
145
- response = requests.get(url, headers=headers_desktop, allow_redirects=True)
146
- response.url
147
-
148
- pack_title_url = urlparse(response.url).path.split("/")[-1]
149
- pack_info_unauthed = MetadataKakao.get_pack_info_unauthed(pack_title_url)
150
- if pack_info_unauthed:
151
- self.author = pack_info_unauthed["result"]["artist"]
152
-
153
- return pack_title, item_code
154
-
155
111
  def download_stickers_kakao(self) -> Tuple[int, int]:
156
112
  self.auth_token = None
157
113
  if self.opt_cred:
158
114
  self.auth_token = self.opt_cred.kakao_auth_token
159
115
 
160
116
  if urlparse(self.url).netloc == "emoticon.kakao.com":
161
- self.pack_title, item_code = self.get_info_from_share_link(self.url)
117
+ item_code = None
118
+ if self.auth_token is not None:
119
+ hash = urlparse(self.url).path.split("/")[-1]
120
+ item_code = MetadataKakao.get_item_code_from_hash(hash, self.auth_token)
162
121
 
163
- if item_code:
164
- return self.download_animated(item_code)
165
- self.cb.put("Download failed: Cannot download metadata for sticker pack")
166
- return 0, 0
122
+ # Share link redirect to preview link if use desktop headers
123
+ # This allows us to find pack author
124
+ headers_desktop = {"User-Agent": "Chrome"}
125
+
126
+ r = requests.get(self.url, headers=headers_desktop, allow_redirects=True)
127
+
128
+ self.pack_title = urlparse(r.url).path.split("/")[-1]
129
+ pack_info_unauthed = MetadataKakao.get_pack_info_unauthed(self.pack_title)
130
+ if pack_info_unauthed is None:
131
+ self.cb.put(
132
+ "Download failed: Cannot download metadata for sticker pack"
133
+ )
134
+ return 0, 0
135
+
136
+ self.author = pack_info_unauthed["result"]["artist"]
137
+ thumbnail_urls = pack_info_unauthed["result"]["thumbnailUrls"]
167
138
 
168
- if self.url.isnumeric() or self.url.startswith("kakaotalk://store/emoticon/"):
169
- item_code = self.url.replace("kakaotalk://store/emoticon/", "")
139
+ if item_code is None:
140
+ if self.auth_token is None:
141
+ self.cb.put(
142
+ "Warning: Downloading animated sticker requires auth_token"
143
+ )
144
+ else:
145
+ self.cb.put(
146
+ "Warning: auth_token invalid, cannot download animated sticker"
147
+ )
148
+ self.cb.put("Downloading static stickers...")
149
+ self.download_static(thumbnail_urls)
150
+ else:
151
+ return self.download_animated(item_code)
170
152
 
153
+ if self.url.isnumeric():
171
154
  self.pack_title = None
172
155
  if self.auth_token:
173
156
  self.pack_info_authed = MetadataKakao.get_pack_info_authed(
174
- item_code, self.auth_token
157
+ self.url, self.auth_token
175
158
  )
176
159
  if self.pack_info_authed:
177
160
  self.pack_title = self.pack_info_authed["itemUnitInfo"][0]["title"]
@@ -182,7 +165,7 @@ class DownloadKakao(DownloadBase):
182
165
  )
183
166
  self.cb.put("Continuing without getting pack_title")
184
167
 
185
- return self.download_animated(item_code)
168
+ return self.download_animated(self.url)
186
169
 
187
170
  if urlparse(self.url).netloc == "e.kakao.com":
188
171
  self.pack_title = urlparse(self.url).path.split("/")[-1]
@@ -201,7 +184,9 @@ class DownloadKakao(DownloadBase):
201
184
  thumbnail_urls = self.pack_info_unauthed["result"]["thumbnailUrls"]
202
185
 
203
186
  if self.auth_token:
204
- item_code = MetadataKakao.get_item_code(title_ko, self.auth_token)
187
+ item_code = MetadataKakao.get_item_code_from_title(
188
+ title_ko, self.auth_token
189
+ )
205
190
  if item_code:
206
191
  return self.download_animated(item_code)
207
192
  msg = "Warning: Cannot get item code.\n"
@@ -19,7 +19,7 @@ class DownloadViber(DownloadBase):
19
19
  # def __init__(self, *args: Any, **kwargs: Any) -> None:
20
20
  # super().__init__(*args, **kwargs)
21
21
 
22
- def get_pack_info(self, url: str) -> Optional[Tuple[str, str]]:
22
+ def get_pack_info(self, url: str) -> Optional[Tuple[str, str, str]]:
23
23
  r = requests.get(url, allow_redirects=True)
24
24
  soup = BeautifulSoup(r.text, "html.parser")
25
25
 
@@ -41,10 +41,13 @@ class DownloadViber(DownloadBase):
41
41
  title = pack_dict["title"]
42
42
  first_sticker_url = cast(str, pack_dict["stickerFirstItemUrl"])
43
43
  zip_url = "/".join(first_sticker_url.split("/")[:-1]) + ".zip"
44
+ pack_id = pack_dict["id"].split(".")[-1]
44
45
 
45
- return title, zip_url
46
+ return title, zip_url, pack_id
46
47
 
47
- def decompress(self, zip_file: bytes) -> int:
48
+ def decompress(
49
+ self, zip_file: bytes, exts: Optional[Tuple[str, ...]] = None
50
+ ) -> int:
48
51
  with zipfile.ZipFile(BytesIO(zip_file)) as zf:
49
52
  self.cb.put("Unzipping...")
50
53
 
@@ -58,9 +61,14 @@ class DownloadViber(DownloadBase):
58
61
  )
59
62
 
60
63
  for sticker in zf_files:
64
+ ext = Path(sticker).suffix
65
+ if "frame" in sticker or ".db" in sticker or sticker.endswith("/"):
66
+ continue
67
+ if exts is not None and ext not in exts:
68
+ continue
61
69
  num = sticker.split(".")[0][-2:].zfill(3)
62
70
  data = zf.read(sticker)
63
- ext = Path(sticker).suffix
71
+
64
72
  self.cb.put(f"Read {sticker}")
65
73
 
66
74
  out_path = Path(self.out_dir, num + ext)
@@ -76,10 +84,16 @@ class DownloadViber(DownloadBase):
76
84
  if pack_info is None:
77
85
  self.cb.put("Download failed: Cannot get pack info")
78
86
  return 0, 0
79
- title, zip_url = pack_info
87
+ title, zip_url, pack_id = pack_info
80
88
 
89
+ anim_url = f"https://content.cdn.viber.com/stickers/ASVG/{pack_id}.zip"
90
+ anim_file = self.download_file(anim_url)
81
91
  zip_file = self.download_file(zip_url)
82
- count = self.decompress(zip_file)
92
+ if anim_file:
93
+ count = self.decompress(anim_file, (".svg",))
94
+ count += self.decompress(zip_file, (".mp3",))
95
+ else:
96
+ count = self.decompress(zip_file, (".mp3", ".png"))
83
97
 
84
98
  MetadataHandler.set_metadata(self.out_dir, title=title)
85
99
 
File without changes
File without changes
File without changes
File without changes
@@ -52,7 +52,7 @@
52
52
  "kakao": {
53
53
  "full_name": "Download from Kakao",
54
54
  "help": "Download kakao stickers from a URL / ID as input",
55
- "example": "Example: https://e.kakao.com/t/xxxxx OR kakaotalk://store/emoticon/4404400\nOR https://emoticon.kakao.com/items/xxxxx OR 4404400",
55
+ "example": "Example: https://e.kakao.com/t/xxxxx \nOR https://emoticon.kakao.com/items/xxxxx OR 4404400",
56
56
  "address_lbls": "URL address / ID",
57
57
  "metadata_provides": {
58
58
  "title": true,
File without changes
File without changes
@@ -72,23 +72,22 @@ class UploadViber(UploadBase):
72
72
  separate_image_anim=False,
73
73
  )
74
74
 
75
- cover_path_old = MetadataHandler.get_cover(self.opt_output.dir)
76
- if cover_path_old:
77
- cover_path = cover_path_old
75
+ cover_path = MetadataHandler.get_cover(self.opt_output.dir)
76
+ if cover_path is None:
77
+ cover_path = MetadataHandler.get_stickers_present(self.opt_output.dir)[0]
78
+
79
+ if FormatVerify.check_file(cover_path, spec=self.png_cover_spec):
80
+ with open(cover_path, "rb") as f:
81
+ cover_bytes = f.read()
78
82
  else:
79
- cover_path_old = MetadataHandler.get_stickers_present(self.opt_output.dir)[
80
- 0
81
- ]
82
- cover_path = self.opt_output.dir / "cover.png"
83
-
84
- if not FormatVerify.check_file(cover_path_old, spec=self.png_cover_spec):
85
- StickerConvert.convert(
86
- cover_path_old,
83
+ _, _, cover_bytes, _ = StickerConvert.convert( # type: ignore
87
84
  cover_path,
85
+ Path("bytes.png"),
88
86
  self.opt_comp_merged,
89
87
  self.cb,
90
88
  self.cb_return,
91
89
  )
90
+ assert isinstance(cover_bytes, bytes)
92
91
 
93
92
  stickers_total = 0
94
93
  stickers_ok = 0
@@ -126,12 +125,12 @@ class UploadViber(UploadBase):
126
125
  upload_data["description"] = author
127
126
  upload_data["shareable"] = "1"
128
127
 
129
- with open(out_f, "rb") as f, open(cover_path, "rb") as g:
128
+ with open(out_f, "rb") as f:
130
129
  r = requests.post(
131
130
  "https://market.api.viber.com/2/users/custom-sticker-packs/create",
132
131
  files={
133
- "file": ("upload.zip", f),
134
- "file_icon": ("color_icon.png", g),
132
+ "file": ("upload.zip", f.read()),
133
+ "file_icon": ("color_icon.png", cover_bytes),
135
134
  },
136
135
  data=upload_data,
137
136
  )
@@ -6,6 +6,7 @@ from typing import Any, Callable, Dict, Optional
6
6
  from urllib.parse import parse_qs, urlparse
7
7
 
8
8
  import requests
9
+ from bs4 import BeautifulSoup
9
10
 
10
11
  from sticker_convert.job_option import CredOption
11
12
 
@@ -32,10 +33,10 @@ class GetKakaoAuth:
32
33
  self.uuid_c = str(uuid.uuid4())
33
34
  self.device_info = (
34
35
  f"android/30; uuid={self.device_uuid}; ssaid={self.device_ssaid}; "
35
- + 'model=ANDROID-SDK-BUILT-FOR-X86; screen_resolution=1080x1920; sim=310260/1/us; onestore=false; uvc2={"volume":5,"network_operator":"310260","is_roaming":"false","va":[],"brightness":102,"totalMemory":30866040,"batteryPct":1,"webviewVersion":"83.0.4103.106"}'
36
+ + "model=SDK_GPHONE_X86_64; screen_resolution=1080x1920; sim=310260/1/us; onestore=false; uvc3=null"
36
37
  )
37
38
  self.app_platform = "android"
38
- self.app_version_number = "10.0.3"
39
+ self.app_version_number = self.get_version()
39
40
  self.app_language = "en"
40
41
  self.app_version = (
41
42
  f"{self.app_platform}/{self.app_version_number}/{self.app_language}"
@@ -44,7 +45,7 @@ class GetKakaoAuth:
44
45
  self.headers = {
45
46
  "Host": "katalk.kakao.com",
46
47
  "Accept-Language": "en",
47
- "User-Agent": "KT/10.0.3 An/11 en",
48
+ "User-Agent": f"KT/{self.app_version_number} An/11 en",
48
49
  "Device-Info": self.device_info,
49
50
  "A": self.app_version,
50
51
  "C": self.uuid_c,
@@ -52,6 +53,20 @@ class GetKakaoAuth:
52
53
  "Connection": "close",
53
54
  }
54
55
 
56
+ def get_version(self) -> str:
57
+ # It is difficult to get app version number from Google Play
58
+ r = requests.get(
59
+ "https://apkpure.net/kakaotalk-messenger/com.kakao.talk/versions"
60
+ )
61
+ soup = BeautifulSoup(r.text, "html.parser")
62
+ for li in soup.find_all("li"):
63
+ a = li.find("a", class_="dt-version-icon")
64
+ if a is None:
65
+ continue
66
+ if "/kakaotalk-messenger/com.kakao.talk/download/" in a.get("href", ""):
67
+ return a.get("href").split("/")[-1]
68
+ return "25.2.1"
69
+
55
70
  def login(self) -> bool:
56
71
  self.cb_msg("Logging in")
57
72
 
@@ -96,7 +111,6 @@ class GetKakaoAuth:
96
111
  "countryCode": self.country_code,
97
112
  "countryIso": self.country_iso,
98
113
  "phoneNumber": self.phone_number,
99
- "method": "sms",
100
114
  "termCodes": [],
101
115
  "simPhoneNumber": f"+{self.country_code}{self.phone_number}",
102
116
  }
@@ -244,6 +258,21 @@ class GetKakaoAuth:
244
258
  self.cb_msg_block(f"Failed at passcode callback: {response.text}")
245
259
  return False
246
260
 
261
+ return True
262
+
263
+ def skip_restoration(self) -> bool:
264
+ self.cb_msg("Skip restoration")
265
+
266
+ response = requests.post(
267
+ "https://katalk.kakao.com/android/account2/skip-restoration",
268
+ headers=self.headers,
269
+ )
270
+ response_json = json.loads(response.text)
271
+
272
+ if response_json["status"] != 0:
273
+ self.cb_msg_block(f"Failed at skip restoration: {response.text}")
274
+ return False
275
+
247
276
  self.nickname = response_json.get("viewData", {}).get("nickname")
248
277
 
249
278
  if self.nickname is None:
@@ -288,6 +317,7 @@ class GetKakaoAuth:
288
317
  self.enter_phone,
289
318
  self.confirm_device_change,
290
319
  self.passcode_callback,
320
+ self.skip_restoration,
291
321
  self.get_profile,
292
322
  )
293
323
 
@@ -105,7 +105,7 @@ class GetKakaoDesktopAuth:
105
105
  return None, msg
106
106
 
107
107
  auth_token = None
108
- for i in re.finditer(b"\x00authorization\x00", s):
108
+ for i in re.finditer(b"authorization: ", s):
109
109
  auth_token_addr = i.start() + 15
110
110
 
111
111
  auth_token_bytes = s[auth_token_addr : auth_token_addr + 200]
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env python3
2
+ import base64
3
+ import io
2
4
  import json
3
5
  import os
4
6
  import platform
@@ -6,10 +8,11 @@ import shutil
6
8
  import socket
7
9
  import subprocess
8
10
  import time
9
- from typing import Any, Dict, Optional, Union, cast
11
+ from typing import Any, Dict, List, Optional, Union, cast
10
12
 
11
13
  import requests
12
14
  import websocket
15
+ from PIL import Image
13
16
 
14
17
  # References
15
18
  # https://github.com/yeongbin-jo/python-chromedriver-autoinstaller/blob/master/chromedriver_autoinstaller/utils.py
@@ -24,15 +27,32 @@ def get_free_port() -> int:
24
27
 
25
28
 
26
29
  class CRD:
27
- def __init__(self, chrome_bin: str, port: Optional[int] = None):
30
+ def __init__(
31
+ self,
32
+ chrome_bin: str,
33
+ port: Optional[int] = None,
34
+ args: Optional[List[str]] = None,
35
+ ):
28
36
  if port is None:
29
37
  port = get_free_port()
30
38
  self.port = port
31
- launch_cmd = [
39
+
40
+ launch_cmd: List[str] = []
41
+ self.display = None
42
+ if (
43
+ platform.system() == "Linux"
44
+ and os.environ.get("DISPLAY", False) is False
45
+ and shutil.which("xvfb-run")
46
+ ):
47
+ launch_cmd += ["xvfb-run", "--server-args='-screen 0, 1024x768x24'"]
48
+
49
+ launch_cmd += [
32
50
  chrome_bin,
33
51
  f"--remote-debugging-port={port}",
34
- f"--remote-allow-origins=http://localhost:{port}",
52
+ f"--remote-allow-origins=http://127.0.0.1:{port}",
35
53
  ]
54
+ if args:
55
+ launch_cmd += args
36
56
 
37
57
  # Adding --no-sandbox in Windows may cause Signal fail to launch
38
58
  # https://github.com/laggykiller/sticker-convert/issues/274
@@ -40,7 +60,7 @@ class CRD:
40
60
  platform.system() != "Windows"
41
61
  and "geteuid" in dir(os)
42
62
  and os.geteuid() == 0
43
- ):
63
+ ) or os.path.isfile("/.dockerenv"):
44
64
  launch_cmd.append("--no-sandbox")
45
65
 
46
66
  self.chrome_proc = subprocess.Popen(launch_cmd)
@@ -89,12 +109,17 @@ class CRD:
89
109
  return chrome_bin
90
110
  return None
91
111
 
92
- def connect(self):
112
+ def connect(self, target_id: int = 0):
93
113
  self.cmd_id = 1
94
114
  r = None
115
+ targets: List[Any] = []
95
116
  for _ in range(30):
96
117
  try:
97
- r = requests.get(f"http://localhost:{self.port}/json")
118
+ r = requests.get(f"http://127.0.0.1:{self.port}/json")
119
+ targets = json.loads(r.text)
120
+ if len(targets) == 0:
121
+ time.sleep(1)
122
+ continue
98
123
  break
99
124
  except requests.exceptions.ConnectionError:
100
125
  time.sleep(1)
@@ -102,17 +127,12 @@ class CRD:
102
127
  if r is None:
103
128
  raise RuntimeError("Cannot connect to chrome debugging port")
104
129
 
105
- targets = json.loads(r.text)
106
- for _ in range(30):
107
- if len(targets) == 0:
108
- time.sleep(1)
109
- else:
110
- break
111
-
112
130
  if len(targets) == 0:
113
131
  raise RuntimeError("Cannot create websocket connection with debugger")
114
132
 
115
- self.ws = websocket.create_connection(targets[0]["webSocketDebuggerUrl"]) # type: ignore
133
+ self.ws = websocket.create_connection( # type: ignore
134
+ targets[target_id]["webSocketDebuggerUrl"]
135
+ )
116
136
 
117
137
  def send_cmd(self, command: Dict[Any, Any]) -> Union[str, bytes]:
118
138
  if command.get("id") is None:
@@ -138,6 +158,27 @@ class CRD:
138
158
  command["params"]["contextId"] = context_id
139
159
  return self.send_cmd(command)
140
160
 
161
+ def set_transparent_bg(self) -> Union[str, bytes]:
162
+ command: Dict[str, Any] = {
163
+ "id": self.cmd_id,
164
+ "method": "Emulation.setDefaultBackgroundColorOverride",
165
+ "params": {"color": {"r": 0, "g": 0, "b": 0, "a": 0}},
166
+ }
167
+ return self.send_cmd(command)
168
+
169
+ def screenshot(self, clip: Optional[Dict[str, int]] = None):
170
+ command: Dict[str, Any] = {
171
+ "id": self.cmd_id,
172
+ "method": "Page.captureScreenshot",
173
+ "params": {},
174
+ }
175
+ if clip:
176
+ command["params"]["clip"] = clip
177
+ result = self.send_cmd(command)
178
+ return Image.open(
179
+ io.BytesIO(base64.b64decode(json.loads(result)["result"]["data"]))
180
+ )
181
+
141
182
  def get_curr_url(self) -> str:
142
183
  r = self.exec_js("window.location.href")
143
184
  return cast(
@@ -148,6 +189,26 @@ class CRD:
148
189
  command = {"id": self.cmd_id, "method": "Page.navigate", "params": {"url": url}}
149
190
  self.send_cmd(command)
150
191
 
192
+ def open_html_str(self, html: str):
193
+ command: Dict[str, Any] = {
194
+ "id": self.cmd_id,
195
+ "method": "Page.navigate",
196
+ "params": {"url": "about:blank"},
197
+ }
198
+ result = cast(str, self.send_cmd(command))
199
+ frame_id = json.loads(result).get("result", {}).get("frameId", None)
200
+ if frame_id is None:
201
+ raise RuntimeError(f"Cannot navigate to about:blank ({result})")
202
+
203
+ self.exec_js('document.getElementsByTagName("html")[0].remove()')
204
+
205
+ command = {
206
+ "id": self.cmd_id,
207
+ "method": "Page.setDocumentContent",
208
+ "params": {"frameId": frame_id, "html": html},
209
+ }
210
+ self.send_cmd(command)
211
+
151
212
  def runtime_enable(self):
152
213
  command = {
153
214
  "method": "Runtime.enable",
@@ -169,3 +230,5 @@ class CRD:
169
230
  def close(self):
170
231
  self.ws.close()
171
232
  self.chrome_proc.kill()
233
+ if self.display:
234
+ self.display.stop()
@@ -17,6 +17,7 @@ RELATED_EXTENSIONS = (
17
17
  ".tgs",
18
18
  ".lottie",
19
19
  ".json",
20
+ ".svg",
20
21
  ".mp4",
21
22
  ".mkv",
22
23
  ".mov",
@@ -3,16 +3,22 @@ from __future__ import annotations
3
3
 
4
4
  import json
5
5
  import mmap
6
+ import warnings
6
7
  from decimal import ROUND_HALF_UP, Decimal
7
8
  from fractions import Fraction
8
9
  from io import BytesIO
9
- from math import gcd
10
+ from math import ceil, gcd
10
11
  from pathlib import Path
11
12
  from typing import BinaryIO, List, Optional, Tuple, Union, cast
12
13
 
14
+ from bs4 import BeautifulSoup, XMLParsedAsHTMLWarning
13
15
  from PIL import Image, UnidentifiedImageError
14
16
  from rlottie_python.rlottie_wrapper import LottieAnimation
15
17
 
18
+ from sticker_convert.definitions import SVG_DEFAULT_HEIGHT, SVG_DEFAULT_WIDTH, SVG_SAMPLE_FPS
19
+
20
+ warnings.filterwarnings("ignore", category=XMLParsedAsHTMLWarning)
21
+
16
22
 
17
23
  def lcm(a: int, b: int):
18
24
  return abs(a * b) // gcd(a, b)
@@ -90,11 +96,17 @@ class CodecInfo:
90
96
  self.file_ext = CodecInfo.get_file_ext(file)
91
97
  else:
92
98
  self.file_ext = file_ext
93
- self.fps, self.frames, self.duration = CodecInfo.get_file_fps_frames_duration(
94
- file
95
- )
96
- self.codec = CodecInfo.get_file_codec(file)
97
- self.res = CodecInfo.get_file_res(file)
99
+ if self.file_ext == ".svg":
100
+ self.fps, self.frames, self.duration, self.res = CodecInfo.get_svg_info(
101
+ file
102
+ )
103
+ self.codec = "svg"
104
+ else:
105
+ self.fps, self.frames, self.duration = (
106
+ CodecInfo.get_file_fps_frames_duration(file)
107
+ )
108
+ self.codec = CodecInfo.get_file_codec(file)
109
+ self.res = CodecInfo.get_file_res(file)
98
110
  self.is_animated = self.fps > 1
99
111
 
100
112
  @staticmethod
@@ -435,3 +447,38 @@ class CodecInfo:
435
447
  if CodecInfo.get_file_frames(file, check_anim=True) > 1:
436
448
  return True
437
449
  return False
450
+
451
+ @staticmethod
452
+ def get_svg_info(
453
+ file: Union[Path, bytes],
454
+ ) -> Tuple[float, int, int, Tuple[int, int]]:
455
+ if isinstance(file, Path):
456
+ with open(file) as f:
457
+ svg = f.read()
458
+ else:
459
+ svg = file.decode()
460
+
461
+ soup = BeautifulSoup(svg, "html.parser")
462
+ svg_tag = soup.find_all("svg")[0]
463
+ width = int(svg_tag.get("width", SVG_DEFAULT_WIDTH))
464
+ height = int(svg_tag.get("height", SVG_DEFAULT_HEIGHT))
465
+
466
+ animate_elements = [*soup.find_all("animate")] + [
467
+ *soup.find_all("animateTransform")
468
+ ]
469
+ duration = 0
470
+ for element in animate_elements:
471
+ dur = cast(str, element.get("dur"))
472
+ if dur.endswith("s"):
473
+ duration = int(max(duration, float(dur[:-1]) * 1000))
474
+ elif dur.endswith("ms"):
475
+ duration = int(max(duration, float(dur[:-2])))
476
+
477
+ if duration != 0:
478
+ fps = SVG_SAMPLE_FPS
479
+ frames = ceil(fps * duration / 1000)
480
+ else:
481
+ fps = 0
482
+ frames = 1
483
+
484
+ return fps, frames, duration, (width, height)
@@ -22,9 +22,7 @@ class UrlDetect:
22
22
  ):
23
23
  return "line"
24
24
 
25
- if domain in ("e.kakao.com", "emoticon.kakao.com") or url.startswith(
26
- "kakaotalk://store/emoticon/"
27
- ):
25
+ if domain in ("e.kakao.com", "emoticon.kakao.com"):
28
26
  return "kakao"
29
27
 
30
28
  if domain == "stickers.viber.com":
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
- __version__ = "2.11.8"
3
+ __version__ = "2.12.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sticker-convert
3
- Version: 2.11.8
3
+ Version: 2.12.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>
@@ -374,7 +374,6 @@ 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
- Requires-Dist: mini-racer~=0.12.4
378
377
  Requires-Dist: numpy>=1.22.4
379
378
  Requires-Dist: Pillow~=11.1.0
380
379
  Requires-Dist: pyoxipng~=9.0.0
@@ -437,7 +436,7 @@ Dynamic: license-file
437
436
  | [Telegram](docs/guide_telegram.md) | ✅ (Require `token` or telethon) | ✅ (Require `token` & `user_id` or telethon or manually) |
438
437
  | [WhatsApp](docs/guide_whatsapp.md) | ⭕ (By Android or WhatsApp Web) | ⭕ (Create `.wastickers`, import by Sticker Maker) |
439
438
  | [Line](docs/guide_line.md) | ✅ | 🚫 (Need to submit for manual approval) |
440
- | [Kakao](docs/guide_kakao.md) | ✅ (Need 'share link' for animated) | 🚫 (Need to submit for manual approval) |
439
+ | [Kakao](docs/guide_kakao.md) | ✅ (Need 'auth_token' for animated) | 🚫 (Need to submit for manual approval) |
441
440
  | [Viber](docs/guide_viber.md) | ✅ | ✅ (Require `viber_auth`) |
442
441
  | [Discord](docs/guide_discord.md) | ✅ (Require `token`) | 🚫 |
443
442
  | [iMessage](docs/guide_imessage.md) | 🚫 | ⭕ (Create Xcode stickerpack project for sideload) |
@@ -462,7 +461,7 @@ Dynamic: license-file
462
461
  - For more information: https://github.com/doubleplusc/Line-sticker-downloader
463
462
  - Upload: Not supported. You need to manually submit sticker pack for approval before you can use in app.
464
463
  - Kakao
465
- - Download: Supported (e.g. `https://e.kakao.com/t/xxxxx` OR `kakaotalk://store/emoticon/4404400` OR `https://emoticon.kakao.com/items/xxxxx` OR `4404400`). It is rather complicated, learn more from [docs/guide_kakao.md](docs/guide_kakao.md)
464
+ - Download: Supported (e.g. `https://e.kakao.com/t/xxxxx` OR `https://emoticon.kakao.com/items/xxxxx` OR `4404400`). It is rather complicated, learn more from [docs/guide_kakao.md](docs/guide_kakao.md)
466
465
  - Upload: Not supported. You need to manually submit sticker pack for approval before you can use in app.
467
466
  - Viber
468
467
  - Download: Supported (e.g. `https://stickers.viber.com/pages/example` OR `https://stickers.viber.com/pages/custom-sticker-packs/example`)
@@ -550,7 +549,7 @@ Input options:
550
549
  OR https://line.me/S/sticker/1234/?lang=en OR line://shop/detail/1234 OR 1234)
551
550
  --download-kakao DOWNLOAD_KAKAO
552
551
  Download kakao stickers from a URL / ID as input
553
- (Example: https://e.kakao.com/t/xxxxx OR kakaotalk://store/emoticon/4404400
552
+ (Example: https://e.kakao.com/t/xxxxx
554
553
  OR https://emoticon.kakao.com/items/xxxxx OR 4404400)
555
554
  --download-viber DOWNLOAD_VIBER
556
555
  Download viber stickers from a URL as input
@@ -1,20 +1,20 @@
1
1
  sticker_convert/__init__.py,sha256=iQnv6UOOA69c3soAn7ZOnAIubTIQSUxtq1Uhh8xRWvU,102
2
2
  sticker_convert/__main__.py,sha256=elDCMvU27letiYs8jPUpxaCq5puURnvcDuqGsAAb6_w,592
3
3
  sticker_convert/cli.py,sha256=MWzq9QJJ7HYBD73DmSgRIPo-u58mTGEIoUUpJKNjI0Q,22358
4
- sticker_convert/converter.py,sha256=aK06RPz5DQb5p-8IvsXqZQrvL6miKt4otX9vcdXBfqc,36784
5
- sticker_convert/definitions.py,sha256=ZhP2ALCEud-w9ZZD4c3TDG9eHGPZyaAL7zPUsJAbjtE,2073
4
+ sticker_convert/converter.py,sha256=Dnl5nkvSgIvfbcKp0n4ytoW6ZeHim5dNnrhAs3MZXx8,39423
5
+ sticker_convert/definitions.py,sha256=BqROmOvqIqw8ANaqZXIxJAXGD0HgjAvCfFouQ4SaWHc,2254
6
6
  sticker_convert/gui.py,sha256=D9L-lFif4P4MANglis_wpjkzlUuGPuq8AybWGyPVP8E,33824
7
7
  sticker_convert/job.py,sha256=gEd6yF8WPUR1_7q9SVhnwdD5usrDxwowNnR5q9uS8Yc,28011
8
8
  sticker_convert/job_option.py,sha256=NysZtKVVG7EzRkLnVaeYJY0uNlYKFmjoststB1cvVrY,8020
9
- sticker_convert/version.py,sha256=IGFf6wyoIrB4Ac0ZyUG26XAIcysi37r6vA4pKRslFp0,47
9
+ sticker_convert/version.py,sha256=5PwAcytiNCTA-jXWUYxsOwxsKuNWAlGiQx6_QL36xdI,47
10
10
  sticker_convert/downloaders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  sticker_convert/downloaders/download_base.py,sha256=MI5pCT_tkfoaFlrD1oNynDj1Rv1CK0APuNVElTEAEis,5110
12
12
  sticker_convert/downloaders/download_discord.py,sha256=6AFpLAYL2hRvVcsqUtzDUC31U66U02ZcnKXDnZRi2jk,3496
13
- sticker_convert/downloaders/download_kakao.py,sha256=qc2Ldv_zrM6H_o0dVI8Qp8xuakTNuEYPwqFfC7l_ehc,12530
13
+ sticker_convert/downloaders/download_kakao.py,sha256=kcWwyid76Db7QR8MqhHJuYONnIY9zOHRUBcNcae1VYI,12465
14
14
  sticker_convert/downloaders/download_line.py,sha256=c-hTzEarGQP0b-pnPl29NuSKcaZWUeRo_94YpJzz72M,17911
15
15
  sticker_convert/downloaders/download_signal.py,sha256=3wv-BLd4qggly4AdtwV8f3vUpCVZ-8GnoPLoWngY3Pk,3728
16
16
  sticker_convert/downloaders/download_telegram.py,sha256=iGmOcVjU2Ai19t1lyDY2JhQIc--O5XMyNCGXZAA4DVY,2028
17
- sticker_convert/downloaders/download_viber.py,sha256=SKWyrd2E2QH3aOvMJT2YEVLiCU-jqC-S4hKIrdFvG34,3161
17
+ sticker_convert/downloaders/download_viber.py,sha256=ApHy4sDUOmVwplHfLbso_gyOKn9zccqvRnHENzR_UUM,3790
18
18
  sticker_convert/gui_components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  sticker_convert/gui_components/gui_utils.py,sha256=okho2cA1Scem_m6rPiYifreFzpFrM21-yUkiAv64EUI,3431
20
20
  sticker_convert/gui_components/frames/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -73,7 +73,7 @@ sticker_convert/resources/appicon.png,sha256=6XBEQz7PnerqS43aRkwpWolFG4WvKMuQ-st
73
73
  sticker_convert/resources/compression.json,sha256=hZY906wUmdAuflVhUowm9EW6h40BzuD4LBNe-Mpd9V8,14184
74
74
  sticker_convert/resources/emoji.json,sha256=q9DRFdVJfm7feZW7ZM6Xa5P1QsMvrn0PbBVU9jKcJKI,422720
75
75
  sticker_convert/resources/help.json,sha256=oI8OIB0aFKgDw4bTKT69zDNshOjf0-bM5el3nbJTzMs,7225
76
- sticker_convert/resources/input.json,sha256=NUyIpOGPtP_goDi8FwmbJPuCyLYtDDy83vTpzz5lnb0,3895
76
+ sticker_convert/resources/input.json,sha256=BYHYDqMLZuQnA3MF0ViudGpkSuAG8OMS7L-7hxC3K3o,3858
77
77
  sticker_convert/resources/memdump_linux.sh,sha256=YbdX5C5RyCnoeDUE6JgTo8nQXKhrUw5-kFDx5bQp9tY,651
78
78
  sticker_convert/resources/memdump_windows.ps1,sha256=CfyNSSEW3HJOkTu-mKrP3qh5aprN-1VCBfj-R1fELA0,302
79
79
  sticker_convert/resources/output.json,sha256=SC_3vtEBJ79R7q0vy_p33eLsGFEyBsSOai0Hy7mLqG8,2208
@@ -82,16 +82,16 @@ sticker_convert/uploaders/compress_wastickers.py,sha256=Rgl3WzIDJn7oa00nNqeBFDqI
82
82
  sticker_convert/uploaders/upload_base.py,sha256=uQupPn6r4zrlAzpKzzX7CgvZb69ATyrwPKahWOQj0ds,1203
83
83
  sticker_convert/uploaders/upload_signal.py,sha256=k9XS6oU0gYCXEph-LzWvUP3IDINAVaQzzzNgDRp_YNM,7443
84
84
  sticker_convert/uploaders/upload_telegram.py,sha256=WlUyLJlW83XZz6RhA76jHMXA6TNUIEVbPwhi14RTnds,12482
85
- sticker_convert/uploaders/upload_viber.py,sha256=kRvZZCv5WcalOJvz9Mdl2yqi3L5n4joovA39WMWn4qs,6420
85
+ sticker_convert/uploaders/upload_viber.py,sha256=E5gXxPF22TyfSyOEe1uxnbonCMgH8ma3WTm0ecJ3yxE,6444
86
86
  sticker_convert/uploaders/xcode_imessage.py,sha256=iTTT8gDYOTNkKqXeSWUBuWfxu7xeE418t2Z1YQFR5L0,11365
87
87
  sticker_convert/utils/callback.py,sha256=spYUGlklOs1yPZAxoqwOWgR1sdimpfM8a27if3TaVYk,6155
88
- sticker_convert/utils/chrome_remotedebug.py,sha256=AuAb-wMHiu7RDZ0YWD-WvIUc2CoR_TLsPVj4cdI2Kuw,5056
88
+ sticker_convert/utils/chrome_remotedebug.py,sha256=jxqH2LktA9UVJW055QeHecVRkc8Hl7KlUcxi_srhDXk,7143
89
89
  sticker_convert/utils/emoji.py,sha256=AqB26JY-PkYzNwPLReSnqLiQKe-bR9UXnLclAbgubJ8,367
90
90
  sticker_convert/utils/process.py,sha256=EAQZ9WpiKmkvToIv8G1HNY4V7m0jXyyePTmeP2XOZzE,4688
91
- sticker_convert/utils/url_detect.py,sha256=bgW7tM3_j8zv4uKPPhyyOkYoNdljUdqDkbyBMqAZoEI,947
91
+ sticker_convert/utils/url_detect.py,sha256=vCbhQbcW1X_UtdfQlICGY8pMX34KQ6sCtDJZbp0NrSg,876
92
92
  sticker_convert/utils/auth/get_discord_auth.py,sha256=i5SaXU9Ly6Ci_iBYCYIPcQB5FaAehi9DidB4jbKJMVg,4215
93
- sticker_convert/utils/auth/get_kakao_auth.py,sha256=ipAZ1DUd5CMTpUoxRXHVOFC3DKIpxwxpTYAfrOJ6UZ8,9829
94
- sticker_convert/utils/auth/get_kakao_desktop_auth.py,sha256=VbEec-yujlLJOBeM55Rm5deu4YE9G9Oi_AKTwOsmUMg,5815
93
+ sticker_convert/utils/auth/get_kakao_auth.py,sha256=Wok5sp0GGBgTwwlfYB7lVq82ndBAOGTcqsWEaWAXFNE,10760
94
+ sticker_convert/utils/auth/get_kakao_desktop_auth.py,sha256=BBfFI08cVOmi6akenQwMI0Uiatmn76wI8QYHnbvopxg,5809
95
95
  sticker_convert/utils/auth/get_line_auth.py,sha256=8l8ha2vQmk3rHGvDE7PkcxQXbH3oe62LKbI3qVUtvqc,2196
96
96
  sticker_convert/utils/auth/get_signal_auth.py,sha256=oDTcIUcRM8_zfmR6UoBvzBhIscwLRe7n2zw4aw0j8_Q,4564
97
97
  sticker_convert/utils/auth/get_viber_auth.py,sha256=mUTrcxq5bTrzSXEVaeTPqVQIdZdwvIhrbMgBUb7dU30,8173
@@ -100,16 +100,16 @@ sticker_convert/utils/auth/telethon_setup.py,sha256=3hH0KglsotFBxT3gwFjHiDuREyah
100
100
  sticker_convert/utils/files/cache_store.py,sha256=etfe614OAhAyrnM5fGeESKq6R88YLNqkqkxSzEmZ0V0,1047
101
101
  sticker_convert/utils/files/json_manager.py,sha256=Vr6pZJdLMkrJJWN99210aduVHb0ILyf0SSTaw4TZqgc,541
102
102
  sticker_convert/utils/files/json_resources_loader.py,sha256=flZFixUXRTrOAhvRQpuSQgmJ69yXL94sxukcowLT1JQ,1049
103
- sticker_convert/utils/files/metadata_handler.py,sha256=UNCfsbAmN-s4LhMUGlAj-CVCsoDCv4BaP5yQG7R81Jk,10108
103
+ sticker_convert/utils/files/metadata_handler.py,sha256=TUseSpKsFLoqqFf7YFENzkwDYSDKPiMCoEp04ebnVr8,10120
104
104
  sticker_convert/utils/files/run_bin.py,sha256=C_KKGtMUTajJnKo4Ia9e6WuWCXSQRdGsVPG-r5GXT_I,1784
105
105
  sticker_convert/utils/files/sanitize_filename.py,sha256=HBklPGsHRJjFQUIC5rYTQsUrsuTtezZXIEA8CPhLP8A,2156
106
106
  sticker_convert/utils/media/apple_png_normalize.py,sha256=LbrQhc7LlYX4I9ek4XJsZE4l0MygBA1jB-PFiYLEkzk,3657
107
- sticker_convert/utils/media/codec_info.py,sha256=XoEWBfPWTzr4zSVQIU1XF1yh5viHxH5FytNEpdZR38c,14874
107
+ sticker_convert/utils/media/codec_info.py,sha256=_F_iV3k2GYtb8vYZUSKrWl9E7XEW6LDywbnJ-DM6kbk,16476
108
108
  sticker_convert/utils/media/decrypt_kakao.py,sha256=4wq9ZDRnFkx1WmFZnyEogBofiLGsWQM_X69HlA36578,1947
109
109
  sticker_convert/utils/media/format_verify.py,sha256=oM32P186tWe9YxvBQRPr8D3FEmBN3b2rEe_2S_MwxyQ,6236
110
- sticker_convert-2.11.8.dist-info/licenses/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
111
- sticker_convert-2.11.8.dist-info/METADATA,sha256=D1g7_dZwU7hJRhNyX7MPA4Elw4l0FfkkYxhyH1w2lTU,53594
112
- sticker_convert-2.11.8.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
113
- sticker_convert-2.11.8.dist-info/entry_points.txt,sha256=MNJ7XyC--ugxi5jS1nzjDLGnxCyLuaGdsVLnJhDHCqs,66
114
- sticker_convert-2.11.8.dist-info/top_level.txt,sha256=r9vfnB0l1ZnH5pTH5RvkobnK3Ow9m0RsncaOMAtiAtk,16
115
- sticker_convert-2.11.8.dist-info/RECORD,,
110
+ sticker_convert-2.12.0.dist-info/licenses/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
111
+ sticker_convert-2.12.0.dist-info/METADATA,sha256=KBvqYSm9HFUCkPrFOqZ3RDVKCI6916VpywSKT1JvwsE,53484
112
+ sticker_convert-2.12.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
113
+ sticker_convert-2.12.0.dist-info/entry_points.txt,sha256=MNJ7XyC--ugxi5jS1nzjDLGnxCyLuaGdsVLnJhDHCqs,66
114
+ sticker_convert-2.12.0.dist-info/top_level.txt,sha256=r9vfnB0l1ZnH5pTH5RvkobnK3Ow9m0RsncaOMAtiAtk,16
115
+ sticker_convert-2.12.0.dist-info/RECORD,,