sticker-convert 2.9.1__py3-none-any.whl → 2.9.3__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -92,10 +92,10 @@ class DownloadBase:
92
92
  response = requests.get(
93
93
  url, stream=True, allow_redirects=True, **kwargs
94
94
  )
95
- total_length = int(response.headers.get("content-length")) # type: ignore
96
-
97
95
  if not response.ok:
98
96
  return b""
97
+ total_length = int(response.headers.get("content-length")) # type: ignore
98
+
99
99
  self.cb.put(f"Downloading {url}")
100
100
 
101
101
  if show_progress:
@@ -3,16 +3,13 @@ from __future__ import annotations
3
3
 
4
4
  import itertools
5
5
  import json
6
- import re
7
- import zipfile
8
- from io import BytesIO
9
6
  from pathlib import Path
10
7
  from typing import Any, List, Optional, Tuple, cast
11
8
  from urllib.parse import urlparse
12
9
 
13
10
  import requests
14
11
  from bs4 import BeautifulSoup
15
- from bs4.element import Tag
12
+ from py_mini_racer import MiniRacer
16
13
 
17
14
  from sticker_convert.downloaders.download_base import DownloadBase
18
15
  from sticker_convert.job_option import CredOption
@@ -20,94 +17,27 @@ from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
20
17
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
21
18
  from sticker_convert.utils.media.decrypt_kakao import DecryptKakao
22
19
 
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
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
+ """
42
38
 
43
39
 
44
40
  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.content.decode("utf-8", "ignore"), "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[bracket_start_pos + 1 : func_end_pos]
76
- js = js.split(";")[0]
77
-
78
- minus_num_regex = re.search(r"\-(.*?)\^", js)
79
- if not minus_num_regex:
80
- return None, None
81
- minus_num_str = minus_num_regex.group(1)
82
- if not minus_num_str.isnumeric():
83
- return None, None
84
- minus_num = int(minus_num_str)
85
-
86
- xor_num_regex = re.search(r"\^(.*?)\)", js)
87
- if not xor_num_regex:
88
- return None, None
89
- xor_num_str = xor_num_regex.group(1)
90
- if not xor_num_str.isnumeric():
91
- return None, None
92
- xor_num = int(xor_num_str)
93
-
94
- item_code = str(int(item_code_fake) - minus_num ^ xor_num)
95
-
96
- # https://github.com/Nuitka/Nuitka/issues/385
97
- # js2py not working if compiled by nuitka
98
- # web2app_start_pos = js.find("daumtools.web2app(")
99
- # js = js[:web2app_start_pos] + "return a;}"
100
- # get_item_code = js2py.eval_js(js) # type: ignore
101
- # kakao_scheme_link = cast(
102
- # str,
103
- # get_item_code(
104
- # "kakaotalk://store/emoticon/${i}?referer=share_link", item_code_fake
105
- # ),
106
- # )
107
- # item_code = urlparse(kakao_scheme_link).path.split("/")[-1]
108
-
109
- return pack_title, item_code
110
-
111
41
  @staticmethod
112
42
  def get_item_code(title_ko: str, auth_token: str) -> Optional[str]:
113
43
  headers = {
@@ -177,15 +107,41 @@ class DownloadKakao(DownloadBase):
177
107
  self.pack_info_unauthed: Optional[dict[str, Any]] = None
178
108
  self.pack_info_authed: Optional[dict[str, Any]] = None
179
109
 
110
+ def get_info_from_share_link(self, url: str) -> Tuple[Optional[str], Optional[str]]:
111
+ headers = {"User-Agent": "Android"}
112
+
113
+ response = requests.get(url, headers=headers)
114
+ soup = BeautifulSoup(response.content.decode("utf-8", "ignore"), "html.parser")
115
+
116
+ pack_title_tag = soup.find("title") # type: ignore
117
+ if not pack_title_tag:
118
+ return None, None
119
+
120
+ pack_title: str = pack_title_tag.string # type: ignore
121
+
122
+ js = ""
123
+ for script_tag in soup.find_all("script"):
124
+ js = script_tag.string
125
+ if js and "daumtools.web2app" in js:
126
+ break
127
+ if "daumtools.web2app" not in js:
128
+ return None, None
129
+
130
+ js = JSINJECT + js
131
+
132
+ ctx = MiniRacer()
133
+ kakao_url = cast(str, ctx.eval(js))
134
+ item_code = urlparse(kakao_url).path.split("/")[2]
135
+
136
+ return pack_title, item_code
137
+
180
138
  def download_stickers_kakao(self) -> bool:
181
139
  self.auth_token = None
182
140
  if self.opt_cred:
183
141
  self.auth_token = self.opt_cred.kakao_auth_token
184
142
 
185
143
  if urlparse(self.url).netloc == "emoticon.kakao.com":
186
- self.pack_title, item_code = MetadataKakao.get_info_from_share_link(
187
- self.url
188
- )
144
+ self.pack_title, item_code = self.get_info_from_share_link(self.url)
189
145
 
190
146
  if item_code:
191
147
  return self.download_animated(item_code)
@@ -268,14 +224,6 @@ class DownloadKakao(DownloadBase):
268
224
  self.out_dir, title=self.pack_title, author=self.author
269
225
  )
270
226
 
271
- success = self.download_animated_zip(item_code)
272
- if not success:
273
- self.cb.put("Trying to download one by one")
274
- success = self.download_animated_files(item_code)
275
-
276
- return success
277
-
278
- def download_animated_files(self, item_code: str) -> bool:
279
227
  play_exts = [".webp", ".gif", ".png", ""]
280
228
  play_types = ["emot", "emoji", ""] # emot = normal; emoji = mini
281
229
  play_path_format = None
@@ -376,46 +324,6 @@ class DownloadKakao(DownloadBase):
376
324
 
377
325
  return True
378
326
 
379
- def download_animated_zip(self, item_code: str) -> bool:
380
- pack_url = f"http://item.kakaocdn.net/dw/{item_code}.file_pack.zip"
381
-
382
- zip_file = self.download_file(pack_url)
383
- if zip_file:
384
- self.cb.put(f"Downloaded {pack_url}")
385
- else:
386
- self.cb.put(f"Cannot download {pack_url}")
387
- return False
388
-
389
- with zipfile.ZipFile(BytesIO(zip_file)) as zf:
390
- self.cb.put("Unzipping...")
391
- self.cb.put(
392
- (
393
- "bar",
394
- None,
395
- {"set_progress_mode": "determinate", "steps": len(zf.namelist())},
396
- )
397
- )
398
-
399
- for num, f_path in enumerate(sorted(zf.namelist())):
400
- ext = Path(f_path).suffix
401
-
402
- if ext in (".gif", ".webp"):
403
- data = DecryptKakao.xor_data(zf.read(f_path))
404
- self.cb.put(f"Decrypted {f_path}")
405
- else:
406
- data = zf.read(f_path)
407
- self.cb.put(f"Read {f_path}")
408
-
409
- out_path = Path(self.out_dir, str(num).zfill(3) + ext)
410
- with open(out_path, "wb") as f:
411
- f.write(data)
412
-
413
- self.cb.put("update_bar")
414
-
415
- self.cb.put(f"Finished getting {pack_url}")
416
-
417
- return True
418
-
419
327
  @staticmethod
420
328
  def start(
421
329
  url: str,
@@ -181,3 +181,5 @@ class CredFrame(LabelFrame):
181
181
  self.kakao_get_auth_btn.config(state=state)
182
182
  self.line_cookies_entry.config(state=state)
183
183
  self.line_get_auth_btn.config(state=state)
184
+ self.viber_auth_lbl.config(state=state)
185
+ self.viber_auth_entry.config(state=state)
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
- __version__ = "2.9.1"
3
+ __version__ = "2.9.3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sticker-convert
3
- Version: 2.9.1
3
+ Version: 2.9.3
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>
@@ -367,17 +367,18 @@ License-File: LICENSE
367
367
  Requires-Dist: aiolimiter ~=1.1.0
368
368
  Requires-Dist: anyio ~=4.4.0
369
369
  Requires-Dist: apngasm-python ~=1.3.1
370
- Requires-Dist: av ~=12.1.0
370
+ Requires-Dist: av ~=12.2.0
371
371
  Requires-Dist: beautifulsoup4 ~=4.12.3
372
- Requires-Dist: rookiepy ~=0.5.1
372
+ Requires-Dist: rookiepy ~=0.5.2
373
373
  Requires-Dist: imagequant ~=1.1.1
374
374
  Requires-Dist: memory-tempfile ~=2.2.3
375
375
  Requires-Dist: mergedeep ~=1.3.4
376
+ Requires-Dist: mini-racer ~=0.12.4
376
377
  Requires-Dist: numpy >=1.22.4
377
- Requires-Dist: Pillow ~=10.3.0
378
+ Requires-Dist: Pillow ~=10.4.0
378
379
  Requires-Dist: pyoxipng ~=9.0.0
379
380
  Requires-Dist: python-telegram-bot ~=21.3
380
- Requires-Dist: psutil ~=5.9.8
381
+ Requires-Dist: psutil ~=6.0.0
381
382
  Requires-Dist: PyMemoryEditor ~=1.5.22
382
383
  Requires-Dist: requests ~=2.32.3
383
384
  Requires-Dist: rlottie-python ~=1.3.6
@@ -392,7 +393,7 @@ Requires-Dist: ttkbootstrap-fork-laggykiller ~=1.5.1
392
393
 
393
394
  - A python script for creating, downloading, converting+compressing and uploading stickers from multiple instant messaging applications.
394
395
  - With GUI and CLI that runs on Windows, MacOS and Linux
395
- - Currently supports Signal, Telegram, WhatsApp (Create .wastickers), Line (Download only), Kakao (Download only), Viber (Download only), iMessage (Create Xcode sticker pack project)
396
+ - Currently supports Signal, Telegram, WhatsApp (Create .wastickers), Line (Download only), Kakao (Download only), Viber, iMessage (Create Xcode sticker pack project)
396
397
  - Supports static and animated stickers, with transparency support
397
398
 
398
399
  ## Downloads
@@ -430,7 +431,7 @@ Requires-Dist: ttkbootstrap-fork-laggykiller ~=1.5.1
430
431
  | [WhatsApp](docs/guide_whatsapp.md) | ⭕ (By Android or WhatsApp Web) | ⭕ (Create `.wastickers`, import by Sticker Maker) |
431
432
  | [Line](docs/guide_line.md) | ✅ | 🚫 (Need to submit for manual approval) |
432
433
  | [Kakao](docs/guide_kakao.md) | ✅ (Need 'share link' for animated) | 🚫 (Need to submit for manual approval) |
433
- | [Viber](docs/guide_viber.md) | ✅ | 🚫 (Manually upload through Viber app) |
434
+ | [Viber](docs/guide_viber.md) | ✅ | (Require `viber_auth`) |
434
435
  | [iMessage](docs/guide_imessage.md) | 🚫 | ⭕ (Create Xcode stickerpack project for sideload) |
435
436
 
436
437
  ✅ = Supported ⭕ = Partially supported 🚫 = Not supported
@@ -459,7 +460,7 @@ Requires-Dist: ttkbootstrap-fork-laggykiller ~=1.5.1
459
460
  - Upload: Not supported. You need to manually submit sticker pack for approval before you can use in app.
460
461
  - Viber
461
462
  - Download: Supported (e.g. `https://stickers.viber.com/pages/example` OR `https://stickers.viber.com/pages/custom-sticker-packs/example`)
462
- - Upload: The program can convert images to png with 490x490 for uploading to viber manually. It should be noted that Viber is able to resize images for you, so it may not be necessary to use sticker-convert for creating Viber sticker pack.
463
+ - Upload: Supported. Viber authentication data required for uploading Viber stickers, which could be fetched from Viber Desktop application automatically.
463
464
  - iMessage
464
465
  - Download: Not supported.
465
466
  - Upload: The program can create Xcode project for iMessage sticker pack, which could then be compiled and sideloaded using Xcode.
@@ -6,10 +6,10 @@ sticker_convert/definitions.py,sha256=ZhP2ALCEud-w9ZZD4c3TDG9eHGPZyaAL7zPUsJAbjt
6
6
  sticker_convert/gui.py,sha256=hZOp2SyEYKbCRm9SRTItBqEk724SHtSqK_Txo0wpL8Q,31194
7
7
  sticker_convert/job.py,sha256=4C2WwB3PFldacqI8UPnM10thM087VLEl9wn6P9_LtS0,26128
8
8
  sticker_convert/job_option.py,sha256=yFwHEhW8Gzp9dfiXakkCREfejAIJhiOxwD4Wlg9jcPk,7805
9
- sticker_convert/version.py,sha256=baBhLdXSz6WrhsijOG00KYOzp3-KSIDIG59dfAQPMrQ,46
9
+ sticker_convert/version.py,sha256=-3orOmwxQLXzeq93BjsCFkrDamN1_r5Ng80gy5-PF0w,46
10
10
  sticker_convert/downloaders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- sticker_convert/downloaders/download_base.py,sha256=x18bI2mPpbXRnSmStBHEb1IvN-VPCilOHLQUs6YPUEU,4041
12
- sticker_convert/downloaders/download_kakao.py,sha256=TCygK-LiSkk3yEYZymZsHVwtW5JmQX6JeIneh25XxFA,14984
11
+ sticker_convert/downloaders/download_base.py,sha256=WGEtri16p6GCG7hhQ603pKS8efoB72oCBHTuJ_RVmG0,4041
12
+ sticker_convert/downloaders/download_kakao.py,sha256=kE3LikphcFrFjAf-eJhmkj0Ko_JG9QpWFTp_YiWEzRA,11605
13
13
  sticker_convert/downloaders/download_line.py,sha256=9WzOWujTbZdAqBi52k21OUEfRmcV1loCaJiDmg6dklw,17853
14
14
  sticker_convert/downloaders/download_signal.py,sha256=PfwscdbcEd_5C3Ecs0F8Qc8si1sLzLodAdnsHVwXgac,3063
15
15
  sticker_convert/downloaders/download_telegram.py,sha256=jufMqc78aXOPDr7fQf9ykkNyhQ7KVCp4gRBxs09NgMo,4614
@@ -20,7 +20,7 @@ sticker_convert/gui_components/frames/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCe
20
20
  sticker_convert/gui_components/frames/comp_frame.py,sha256=9k_UntKKi2G_g0byzoj1rdTqOq7q9mcnXiy799bYnr0,7257
21
21
  sticker_convert/gui_components/frames/config_frame.py,sha256=b3X4QAnGpde0OhthXHmjSyU_Yb5tYRUFXmy04Yi8Zmo,4316
22
22
  sticker_convert/gui_components/frames/control_frame.py,sha256=_XiOJ9JPUfiysDghGG04sEVLrXG9TMVlDZ60W0LhYVI,835
23
- sticker_convert/gui_components/frames/cred_frame.py,sha256=QCAmDfSgL-ykGxPsL_9kWFssbtVdrh17IWW28oxzdDg,7429
23
+ sticker_convert/gui_components/frames/cred_frame.py,sha256=AJHxxBdEa-xOteevVBsSfAHtcJklU3pu2TqvZny9o4w,7527
24
24
  sticker_convert/gui_components/frames/input_frame.py,sha256=5Vz1d6J1jkofvvzm43DxeIW_CjWfxa2QPYFnkAAiAfM,5040
25
25
  sticker_convert/gui_components/frames/output_frame.py,sha256=n2WLk22h61DoZli8WbFhd-h2CqWAebDXnBa051JBuOc,4260
26
26
  sticker_convert/gui_components/frames/progress_frame.py,sha256=LWUZg_iL7iiNTfu7N5Ct_pklZdghxihENi7DP9YozOE,4915
@@ -98,9 +98,9 @@ sticker_convert/utils/media/apple_png_normalize.py,sha256=LbrQhc7LlYX4I9ek4XJsZE
98
98
  sticker_convert/utils/media/codec_info.py,sha256=1QfW3wgZ5vOk7T4XtLHYvJK1x8RbASRPSvhKEPkcu9A,15747
99
99
  sticker_convert/utils/media/decrypt_kakao.py,sha256=4wq9ZDRnFkx1WmFZnyEogBofiLGsWQM_X69HlA36578,1947
100
100
  sticker_convert/utils/media/format_verify.py,sha256=MH68GLJfXeL8WFT8emtj355K5BLAtUX64tQ59nugx2c,5673
101
- sticker_convert-2.9.1.dist-info/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
102
- sticker_convert-2.9.1.dist-info/METADATA,sha256=Sim5kcFjuwRWedkvQhuAIyCMmpYRQ-bA3cAIqQiZuFw,51091
103
- sticker_convert-2.9.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
104
- sticker_convert-2.9.1.dist-info/entry_points.txt,sha256=MNJ7XyC--ugxi5jS1nzjDLGnxCyLuaGdsVLnJhDHCqs,66
105
- sticker_convert-2.9.1.dist-info/top_level.txt,sha256=r9vfnB0l1ZnH5pTH5RvkobnK3Ow9m0RsncaOMAtiAtk,16
106
- sticker_convert-2.9.1.dist-info/RECORD,,
101
+ sticker_convert-2.9.3.dist-info/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
102
+ sticker_convert-2.9.3.dist-info/METADATA,sha256=AhL6yrezU5-qhw5E2euXRDGz9fRplznnme-gVs4O0bo,51023
103
+ sticker_convert-2.9.3.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
104
+ sticker_convert-2.9.3.dist-info/entry_points.txt,sha256=MNJ7XyC--ugxi5jS1nzjDLGnxCyLuaGdsVLnJhDHCqs,66
105
+ sticker_convert-2.9.3.dist-info/top_level.txt,sha256=r9vfnB0l1ZnH5pTH5RvkobnK3Ow9m0RsncaOMAtiAtk,16
106
+ sticker_convert-2.9.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (70.3.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5