sticker-convert 2.9.1__py3-none-any.whl → 2.9.2__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,20 @@ 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
6
+ import webbrowser
9
7
  from pathlib import Path
8
+ from tempfile import TemporaryDirectory
10
9
  from typing import Any, List, Optional, Tuple, cast
11
10
  from urllib.parse import urlparse
12
11
 
12
+ import chromedriver_autoinstaller # type: ignore
13
13
  import requests
14
14
  from bs4 import BeautifulSoup
15
- from bs4.element import Tag
15
+ from selenium import webdriver
16
+ from selenium.webdriver.chrome.options import Options
17
+ from selenium.webdriver.common.by import By
18
+ from selenium.webdriver.support import expected_conditions as EC
19
+ from selenium.webdriver.support.wait import WebDriverWait
16
20
 
17
21
  from sticker_convert.downloaders.download_base import DownloadBase
18
22
  from sticker_convert.job_option import CredOption
@@ -20,94 +24,38 @@ from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
20
24
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
21
25
  from sticker_convert.utils.media.decrypt_kakao import DecryptKakao
22
26
 
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
27
+ JSINJECT = """
28
+ class osclass {
29
+ android = true;
30
+ }
31
+ class uaclass {
32
+ os = new osclass();
33
+ }
34
+ class util {
35
+ static userAgent() {
36
+ return new uaclass();
37
+ }
38
+ }
39
+ class daumtools {
40
+ static web2app(dataDict) {
41
+ return dataDict['urlScheme'];
42
+ }
43
+ }
44
+ """
45
+
46
+ HTMLPAGE = """
47
+ <html>
48
+ <body>
49
+ <p id="p1"></p>
50
+ <script>
51
+ {}
52
+ </script>
53
+ </body>
54
+ </html>
55
+ """
42
56
 
43
57
 
44
58
  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
59
  @staticmethod
112
60
  def get_item_code(title_ko: str, auth_token: str) -> Optional[str]:
113
61
  headers = {
@@ -177,15 +125,67 @@ class DownloadKakao(DownloadBase):
177
125
  self.pack_info_unauthed: Optional[dict[str, Any]] = None
178
126
  self.pack_info_authed: Optional[dict[str, Any]] = None
179
127
 
128
+ def get_info_from_share_link(self, url: str) -> Tuple[Optional[str], Optional[str]]:
129
+ headers = {"User-Agent": "Android"}
130
+
131
+ response = requests.get(url, headers=headers)
132
+ soup = BeautifulSoup(response.content.decode("utf-8", "ignore"), "html.parser")
133
+
134
+ pack_title_tag = soup.find("title") # type: ignore
135
+ if not pack_title_tag:
136
+ return None, None
137
+
138
+ pack_title: str = pack_title_tag.string # type: ignore
139
+
140
+ js = ""
141
+ for script_tag in soup.find_all("script"):
142
+ js = script_tag.string
143
+ if js and "daumtools.web2app" in js:
144
+ break
145
+ if "daumtools.web2app" not in js:
146
+ return None, None
147
+
148
+ js = js.replace(
149
+ "daumtools.web2app",
150
+ 'document.getElementById("p1").innerHTML = daumtools.web2app',
151
+ )
152
+ js = JSINJECT + js
153
+
154
+ with TemporaryDirectory() as tempdir:
155
+ html_page_path = Path(tempdir, "page.html")
156
+ html_page = HTMLPAGE.format(js)
157
+ with open(html_page_path, "w+") as f:
158
+ f.write(html_page)
159
+
160
+ try:
161
+ chromedriver_autoinstaller.install()
162
+ chrome_options = Options()
163
+ chrome_options.add_argument("--headless") # type: ignore
164
+ driver = webdriver.Chrome(options=chrome_options)
165
+ driver.get(html_page_path.as_posix())
166
+ wait = WebDriverWait(driver, 10)
167
+ wait.until(EC.text_to_be_present_in_element((By.ID, "p1"), "kakaotalk")) # type: ignore
168
+ kakao_url_elm = driver.find_element(By.ID, "p1")
169
+ kakao_url = cast(str, kakao_url_elm.text) # type: ignore
170
+ driver.close()
171
+ except ValueError:
172
+ webbrowser.open(html_page_path.as_posix())
173
+ prompt = "Chrome not installed, using manual method.\n"
174
+ prompt += "Please copy and paste the url you see in the browser"
175
+ self.cb.put(("ask_str", (prompt,), None))
176
+ kakao_url = cast(str, self.cb_return.get_response())
177
+
178
+ item_code = urlparse(kakao_url).path.split("/")[2]
179
+
180
+ return pack_title, item_code
181
+
180
182
  def download_stickers_kakao(self) -> bool:
181
183
  self.auth_token = None
182
184
  if self.opt_cred:
183
185
  self.auth_token = self.opt_cred.kakao_auth_token
184
186
 
185
187
  if urlparse(self.url).netloc == "emoticon.kakao.com":
186
- self.pack_title, item_code = MetadataKakao.get_info_from_share_link(
187
- self.url
188
- )
188
+ self.pack_title, item_code = self.get_info_from_share_link(self.url)
189
189
 
190
190
  if item_code:
191
191
  return self.download_animated(item_code)
@@ -268,14 +268,6 @@ class DownloadKakao(DownloadBase):
268
268
  self.out_dir, title=self.pack_title, author=self.author
269
269
  )
270
270
 
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
271
  play_exts = [".webp", ".gif", ".png", ""]
280
272
  play_types = ["emot", "emoji", ""] # emot = normal; emoji = mini
281
273
  play_path_format = None
@@ -376,46 +368,6 @@ class DownloadKakao(DownloadBase):
376
368
 
377
369
  return True
378
370
 
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
371
  @staticmethod
420
372
  def start(
421
373
  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.2"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sticker-convert
3
- Version: 2.9.1
3
+ Version: 2.9.2
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,20 +367,22 @@ 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: chromedriver-autoinstaller ~=0.6.4
373
+ Requires-Dist: rookiepy ~=0.5.2
373
374
  Requires-Dist: imagequant ~=1.1.1
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
- 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
385
+ Requires-Dist: selenium ~=4.22.0
384
386
  Requires-Dist: signalstickers-client-fork-laggykiller ~=3.3.0.post2
385
387
  Requires-Dist: sqlcipher3-wheels ~=0.5.2.post1
386
388
  Requires-Dist: tqdm ~=4.66.4
@@ -392,7 +394,7 @@ Requires-Dist: ttkbootstrap-fork-laggykiller ~=1.5.1
392
394
 
393
395
  - A python script for creating, downloading, converting+compressing and uploading stickers from multiple instant messaging applications.
394
396
  - 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)
397
+ - Currently supports Signal, Telegram, WhatsApp (Create .wastickers), Line (Download only), Kakao (Download only), Viber, iMessage (Create Xcode sticker pack project)
396
398
  - Supports static and animated stickers, with transparency support
397
399
 
398
400
  ## Downloads
@@ -430,7 +432,7 @@ Requires-Dist: ttkbootstrap-fork-laggykiller ~=1.5.1
430
432
  | [WhatsApp](docs/guide_whatsapp.md) | ⭕ (By Android or WhatsApp Web) | ⭕ (Create `.wastickers`, import by Sticker Maker) |
431
433
  | [Line](docs/guide_line.md) | ✅ | 🚫 (Need to submit for manual approval) |
432
434
  | [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) |
435
+ | [Viber](docs/guide_viber.md) | ✅ | (Require `viber_auth`) |
434
436
  | [iMessage](docs/guide_imessage.md) | 🚫 | ⭕ (Create Xcode stickerpack project for sideload) |
435
437
 
436
438
  ✅ = Supported ⭕ = Partially supported 🚫 = Not supported
@@ -459,7 +461,7 @@ Requires-Dist: ttkbootstrap-fork-laggykiller ~=1.5.1
459
461
  - Upload: Not supported. You need to manually submit sticker pack for approval before you can use in app.
460
462
  - Viber
461
463
  - 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.
464
+ - Upload: Supported. Viber authentication data required for uploading Viber stickers, which could be fetched from Viber Desktop application automatically.
463
465
  - iMessage
464
466
  - Download: Not supported.
465
467
  - 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=KtHdsdkHNKLnGLbDOZKFG0OukVKP9IeWcQlHYhF_lg0,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=msUUUR-2JdeAO_pQPJBFuA9xVfh1Gwl19WYN_6WLA-Y,13324
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.2.dist-info/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
102
+ sticker_convert-2.9.2.dist-info/METADATA,sha256=09ZbSV3QNFOUGbBUrWrWHZ6Qac7awAGPB5RAFpQiCTk,51071
103
+ sticker_convert-2.9.2.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
104
+ sticker_convert-2.9.2.dist-info/entry_points.txt,sha256=MNJ7XyC--ugxi5jS1nzjDLGnxCyLuaGdsVLnJhDHCqs,66
105
+ sticker_convert-2.9.2.dist-info/top_level.txt,sha256=r9vfnB0l1ZnH5pTH5RvkobnK3Ow9m0RsncaOMAtiAtk,16
106
+ sticker_convert-2.9.2.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