sticker-convert 2.10.7__py3-none-any.whl → 2.10.9__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -702,9 +702,9 @@ class StickerConvert:
702
702
  self._frames_export_apng()
703
703
  else:
704
704
  self._frames_export_png()
705
- elif self.out_f.suffix == ".gif":
705
+ elif self.out_f.suffix in (".gif", ".webp"):
706
706
  self._frames_export_pil_anim()
707
- elif self.out_f.suffix in (".webm", ".mp4", ".mkv", ".webp") or is_animated:
707
+ elif self.out_f.suffix in (".webm", ".mp4", ".mkv") or is_animated:
708
708
  self._frames_export_pyav()
709
709
  else:
710
710
  self._frames_export_pil()
@@ -811,8 +811,9 @@ class StickerConvert:
811
811
  elif self.out_f.suffix == ".webp":
812
812
  im_out = [Image.fromarray(i) for i in self.frames_processed] # type: ignore
813
813
  extra_kwargs["format"] = "WebP"
814
- extra_kwargs["minimize_size"] = True
815
- extra_kwargs["method"] = 6
814
+ # Enabling below is too slow
815
+ # extra_kwargs["minimize_size"] = True
816
+ # extra_kwargs["method"] = 6
816
817
  else:
817
818
  raise RuntimeError(f"Invalid format {self.out_f.suffix}")
818
819
 
@@ -34,18 +34,26 @@ class DownloadBase:
34
34
  retries: int = 3,
35
35
  headers: Optional[dict[Any, Any]] = None,
36
36
  **kwargs: Any,
37
- ) -> None:
37
+ ) -> dict[str, bool]:
38
+ results: dict[str, bool] = {}
38
39
  anyio.run(
39
40
  partial(
40
- self.download_multiple_files_async, targets, retries, headers, **kwargs
41
+ self.download_multiple_files_async,
42
+ targets,
43
+ retries,
44
+ headers,
45
+ results,
46
+ **kwargs,
41
47
  )
42
48
  )
49
+ return results
43
50
 
44
51
  async def download_multiple_files_async(
45
52
  self,
46
53
  targets: List[Tuple[str, Path]],
47
54
  retries: int = 3,
48
55
  headers: Optional[dict[Any, Any]] = None,
56
+ results: Optional[dict[str, bool]] = None,
49
57
  **kwargs: Any,
50
58
  ) -> None:
51
59
  # targets format: [(url1, dest2), (url2, dest2), ...]
@@ -53,44 +61,56 @@ class DownloadBase:
53
61
  ("bar", None, {"set_progress_mode": "determinate", "steps": len(targets)})
54
62
  )
55
63
 
64
+ semaphore = anyio.Semaphore(4)
65
+
56
66
  async with httpx.AsyncClient() as client:
57
67
  async with anyio.create_task_group() as tg:
58
68
  for url, dest in targets:
59
69
  tg.start_soon(
60
70
  self.download_file_async,
71
+ semaphore,
61
72
  client,
62
73
  url,
63
74
  dest,
64
75
  retries,
65
76
  headers,
77
+ results,
66
78
  **kwargs,
67
79
  )
68
80
 
69
81
  async def download_file_async(
70
82
  self,
83
+ semaphore: anyio.Semaphore,
71
84
  client: httpx.AsyncClient,
72
85
  url: str,
73
86
  dest: Path,
74
87
  retries: int = 3,
75
88
  headers: Optional[dict[Any, Any]] = None,
89
+ results: Optional[dict[str, bool]] = None,
76
90
  **kwargs: Any,
77
91
  ) -> None:
78
- self.cb.put(f"Downloading {url}")
79
- for retry in range(retries):
80
- response = await client.get(
81
- url, follow_redirects=True, headers=headers, **kwargs
82
- )
83
-
84
- if response.is_success:
85
- async with await anyio.open_file(dest, "wb+") as f:
86
- await f.write(response.content)
87
- self.cb.put(f"Downloaded {url}")
88
- else:
89
- self.cb.put(
90
- f"Error {response.status_code}: {url} (tried {retry+1}/{retries} times)"
92
+ async with semaphore:
93
+ self.cb.put(f"Downloading {url}")
94
+ success = False
95
+ for retry in range(retries):
96
+ response = await client.get(
97
+ url, follow_redirects=True, headers=headers, **kwargs
91
98
  )
99
+ success = response.is_success
100
+
101
+ if success:
102
+ async with await anyio.open_file(dest, "wb+") as f:
103
+ await f.write(response.content)
104
+ self.cb.put(f"Downloaded {url}")
105
+ else:
106
+ self.cb.put(
107
+ f"Error {response.status_code}: {url} (tried {retry+1}/{retries} times)"
108
+ )
109
+
110
+ if results is not None:
111
+ results[url] = success
92
112
 
93
- self.cb.put("update_bar")
113
+ self.cb.put("update_bar")
94
114
 
95
115
  def download_file(
96
116
  self,
@@ -21,10 +21,10 @@ class DownloadDiscord(DownloadBase):
21
21
  # def __init__(self, *args: Any, **kwargs: Any) -> None:
22
22
  # super().__init__(*args, **kwargs)
23
23
 
24
- def download_stickers_discord(self) -> bool:
24
+ def download_stickers_discord(self) -> Tuple[int, int]:
25
25
  if self.opt_cred is None or self.opt_cred.discord_token == "":
26
26
  self.cb.put("Error: Downloading from Discord requires token")
27
- return False
27
+ return 0, 0
28
28
 
29
29
  gid: Optional[str] = None
30
30
  if self.url.isnumeric():
@@ -36,7 +36,7 @@ class DownloadDiscord(DownloadBase):
36
36
 
37
37
  if gid is None or gid.isnumeric() is False:
38
38
  self.cb.put("Error: Invalid url")
39
- return False
39
+ return 0, 0
40
40
 
41
41
  headers = {
42
42
  "Authorization": self.opt_cred.discord_token,
@@ -68,7 +68,7 @@ class DownloadDiscord(DownloadBase):
68
68
  f_path = Path(self.out_dir, f_name)
69
69
  targets.append((sticker_url, f_path))
70
70
 
71
- self.download_multiple_files(targets)
71
+ results = self.download_multiple_files(targets)
72
72
 
73
73
  server_name = r_json["name"]
74
74
  MetadataHandler.set_metadata(
@@ -78,7 +78,7 @@ class DownloadDiscord(DownloadBase):
78
78
  emoji_dict=emoji_dict if self.input_option == "discord" else None,
79
79
  )
80
80
 
81
- return True
81
+ return sum(results.values()), len(targets)
82
82
 
83
83
  @staticmethod
84
84
  def start(
@@ -86,6 +86,6 @@ class DownloadDiscord(DownloadBase):
86
86
  opt_cred: Optional[CredOption],
87
87
  cb: CallbackProtocol,
88
88
  cb_return: CallbackReturn,
89
- ) -> bool:
89
+ ) -> Tuple[int, int]:
90
90
  downloader = DownloadDiscord(opt_input, opt_cred, cb, cb_return)
91
91
  return downloader.download_stickers_discord()
@@ -34,6 +34,11 @@ class daumtools {
34
34
  return dataDict['urlScheme'];
35
35
  }
36
36
  }
37
+ class document {
38
+ static querySelectorAll(selectors) {
39
+ return [];
40
+ }
41
+ }
37
42
  """
38
43
 
39
44
 
@@ -133,9 +138,21 @@ class DownloadKakao(DownloadBase):
133
138
  kakao_url = cast(str, ctx.eval(js))
134
139
  item_code = urlparse(kakao_url).path.split("/")[2]
135
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
+
136
153
  return pack_title, item_code
137
154
 
138
- def download_stickers_kakao(self) -> bool:
155
+ def download_stickers_kakao(self) -> Tuple[int, int]:
139
156
  self.auth_token = None
140
157
  if self.opt_cred:
141
158
  self.auth_token = self.opt_cred.kakao_auth_token
@@ -146,7 +163,7 @@ class DownloadKakao(DownloadBase):
146
163
  if item_code:
147
164
  return self.download_animated(item_code)
148
165
  self.cb.put("Download failed: Cannot download metadata for sticker pack")
149
- return False
166
+ return 0, 0
150
167
 
151
168
  if self.url.isnumeric() or self.url.startswith("kakaotalk://store/emoticon/"):
152
169
  item_code = self.url.replace("kakaotalk://store/emoticon/", "")
@@ -177,7 +194,7 @@ class DownloadKakao(DownloadBase):
177
194
  self.cb.put(
178
195
  "Download failed: Cannot download metadata for sticker pack"
179
196
  )
180
- return False
197
+ return 0, 0
181
198
 
182
199
  self.author = self.pack_info_unauthed["result"]["artist"]
183
200
  title_ko = self.pack_info_unauthed["result"]["title"]
@@ -197,14 +214,15 @@ class DownloadKakao(DownloadBase):
197
214
  response = False
198
215
 
199
216
  if response is False:
200
- return False
217
+ return 0, 0
201
218
 
202
219
  return self.download_static(thumbnail_urls)
203
220
 
204
221
  self.cb.put("Download failed: Unrecognized URL")
205
- return False
222
+ return 0, 0
206
223
 
207
- def download_static(self, thumbnail_urls: str) -> bool:
224
+ def download_static(self, thumbnail_urls: str) -> Tuple[int, int]:
225
+ headers = {"User-Agent": "Android"}
208
226
  MetadataHandler.set_metadata(
209
227
  self.out_dir, title=self.pack_title, author=self.author
210
228
  )
@@ -215,11 +233,11 @@ class DownloadKakao(DownloadBase):
215
233
  dest = Path(self.out_dir, str(num).zfill(3) + ".png")
216
234
  targets.append((url, dest))
217
235
 
218
- self.download_multiple_files(targets)
236
+ results = self.download_multiple_files(targets, headers=headers)
219
237
 
220
- return True
238
+ return sum(results.values()), len(targets)
221
239
 
222
- def download_animated(self, item_code: str) -> bool:
240
+ def download_animated(self, item_code: str) -> Tuple[int, int]:
223
241
  MetadataHandler.set_metadata(
224
242
  self.out_dir, title=self.pack_title, author=self.author
225
243
  )
@@ -272,7 +290,7 @@ class DownloadKakao(DownloadBase):
272
290
  break
273
291
  if play_ext == "":
274
292
  self.cb.put(f"Failed to determine extension of {item_code}")
275
- return False
293
+ return 0, 0
276
294
  else:
277
295
  play_path_format = f"dw/{item_code}.{play_type}_0##{play_ext}"
278
296
  else:
@@ -308,7 +326,7 @@ class DownloadKakao(DownloadBase):
308
326
  sound_dl_path = Path(self.out_dir, str(num).zfill(3) + sound_ext)
309
327
  targets.append((sound_url, sound_dl_path))
310
328
 
311
- self.download_multiple_files(targets, headers=headers)
329
+ results = self.download_multiple_files(targets, headers=headers)
312
330
 
313
331
  for target in targets:
314
332
  f_path = target[1]
@@ -326,7 +344,7 @@ class DownloadKakao(DownloadBase):
326
344
 
327
345
  self.cb.put(f"Finished getting {item_code}")
328
346
 
329
- return True
347
+ return sum(results.values()), len(targets)
330
348
 
331
349
  @staticmethod
332
350
  def start(
@@ -334,6 +352,6 @@ class DownloadKakao(DownloadBase):
334
352
  opt_cred: Optional[CredOption],
335
353
  cb: CallbackProtocol,
336
354
  cb_return: CallbackReturn,
337
- ) -> bool:
355
+ ) -> Tuple[int, int]:
338
356
  downloader = DownloadKakao(opt_input, opt_cred, cb, cb_return)
339
357
  return downloader.download_stickers_kakao()
@@ -400,13 +400,13 @@ class DownloadLine(DownloadBase):
400
400
 
401
401
  self.cb.put(f"Combined {i.name.replace('-text.png', '.png')}")
402
402
 
403
- def download_stickers_line(self) -> bool:
403
+ def download_stickers_line(self) -> Tuple[int, int]:
404
404
  url_data = MetadataLine.analyze_url(self.url)
405
405
  if url_data:
406
406
  self.pack_id, self.region, self.is_emoji = url_data
407
407
  else:
408
408
  self.cb.put("Download failed: Unsupported URL format")
409
- return False
409
+ return 0, 0
410
410
 
411
411
  if self.is_emoji:
412
412
  metadata = MetadataLine.get_metadata_sticon(self.pack_id, self.region)
@@ -423,7 +423,7 @@ class DownloadLine(DownloadBase):
423
423
  ) = metadata
424
424
  else:
425
425
  self.cb.put("Download failed: Failed to get metadata")
426
- return False
426
+ return 0, 0
427
427
 
428
428
  MetadataHandler.set_metadata(self.out_dir, title=self.title, author=self.author)
429
429
 
@@ -434,7 +434,7 @@ class DownloadLine(DownloadBase):
434
434
  self.cb.put(f"Downloaded {pack_url}")
435
435
  else:
436
436
  self.cb.put(f"Cannot download {pack_url}")
437
- return False
437
+ return 0, 0
438
438
 
439
439
  if self.is_emoji:
440
440
  self.decompress_emoticon(zip_file)
@@ -458,7 +458,7 @@ class DownloadLine(DownloadBase):
458
458
  self.download_multiple_files(custom_sticker_text_urls, headers=self.headers)
459
459
  self.combine_custom_text()
460
460
 
461
- return True
461
+ return len(self.pack_files), len(self.pack_files)
462
462
 
463
463
  @staticmethod
464
464
  def start(
@@ -466,6 +466,6 @@ class DownloadLine(DownloadBase):
466
466
  opt_cred: Optional[CredOption],
467
467
  cb: CallbackProtocol,
468
468
  cb_return: CallbackReturn,
469
- ) -> bool:
469
+ ) -> Tuple[int, int]:
470
470
  downloader = DownloadLine(opt_input, opt_cred, cb, cb_return)
471
471
  return downloader.download_stickers_line()
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  from pathlib import Path
3
- from typing import Dict, Optional
3
+ from typing import Dict, Optional, Tuple
4
4
 
5
5
  import anyio
6
6
  from signalstickers_client.errors import SignalException
@@ -62,10 +62,10 @@ class DownloadSignal(DownloadBase):
62
62
  self.out_dir, title=pack.title, author=pack.author, emoji_dict=emoji_dict
63
63
  )
64
64
 
65
- def download_stickers_signal(self) -> bool:
65
+ def download_stickers_signal(self) -> Tuple[int, int]:
66
66
  if "signal.art" not in self.url:
67
67
  self.cb.put("Download failed: Unrecognized URL format")
68
- return False
68
+ return 0, 0
69
69
 
70
70
  pack_id = self.url.split("#pack_id=")[1].split("&pack_key=")[0]
71
71
  pack_key = self.url.split("&pack_key=")[1]
@@ -74,11 +74,11 @@ class DownloadSignal(DownloadBase):
74
74
  pack = anyio.run(DownloadSignal.get_pack, pack_id, pack_key)
75
75
  except SignalException as e:
76
76
  self.cb.put(f"Failed to download pack due to {repr(e)}")
77
- return False
77
+ return 0, 0
78
78
 
79
79
  self.save_stickers(pack)
80
80
 
81
- return True
81
+ return len(pack.stickers), len(pack.stickers)
82
82
 
83
83
  @staticmethod
84
84
  def start(
@@ -86,6 +86,6 @@ class DownloadSignal(DownloadBase):
86
86
  opt_cred: Optional[CredOption],
87
87
  cb: CallbackProtocol,
88
88
  cb_return: CallbackReturn,
89
- ) -> bool:
89
+ ) -> Tuple[int, int]:
90
90
  downloader = DownloadSignal(opt_input, opt_cred, cb, cb_return)
91
91
  return downloader.download_stickers_signal()
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  from pathlib import Path
3
- from typing import Dict, Optional, Union
3
+ from typing import Dict, Optional, Tuple, Union
4
4
  from urllib.parse import urlparse
5
5
 
6
6
  import anyio
@@ -18,18 +18,18 @@ class DownloadTelegram(DownloadBase):
18
18
  # def __init__(self, *args: Any, **kwargs: Any) -> None:
19
19
  # super().__init__(*args, **kwargs)
20
20
 
21
- def download_stickers_telegram(self) -> bool:
21
+ def download_stickers_telegram(self) -> Tuple[int, int]:
22
22
  self.token = ""
23
23
 
24
24
  if self.opt_cred:
25
25
  self.token = self.opt_cred.telegram_token.strip()
26
26
  if not self.token:
27
27
  self.cb.put("Download failed: Token required for downloading from telegram")
28
- return False
28
+ return 0, 0
29
29
 
30
30
  if not ("telegram.me" in self.url or "t.me" in self.url):
31
31
  self.cb.put("Download failed: Unrecognized URL format")
32
- return False
32
+ return 0, 0
33
33
 
34
34
  self.title = Path(urlparse(self.url).path).name
35
35
 
@@ -37,7 +37,8 @@ class DownloadTelegram(DownloadBase):
37
37
 
38
38
  return anyio.run(self.save_stickers)
39
39
 
40
- async def save_stickers(self) -> bool:
40
+ async def save_stickers(self) -> Tuple[int, int]:
41
+ results: dict[str, bool] = {}
41
42
  timeout = 30
42
43
  application = ( # type: ignore
43
44
  ApplicationBuilder()
@@ -65,7 +66,7 @@ class DownloadTelegram(DownloadBase):
65
66
  self.cb.put(
66
67
  f"Failed to download telegram sticker set {self.title} due to: {e}"
67
68
  )
68
- return False
69
+ return 0, 0
69
70
 
70
71
  self.cb.put(
71
72
  (
@@ -79,14 +80,19 @@ class DownloadTelegram(DownloadBase):
79
80
  )
80
81
 
81
82
  async def download_sticker(
82
- sticker: Union[PhotoSize, Sticker], f_id: str
83
+ sticker: Union[PhotoSize, Sticker], f_id: str, results: dict[str, bool]
83
84
  ) -> None:
84
- sticker_file = await sticker.get_file(
85
- read_timeout=timeout,
86
- write_timeout=timeout,
87
- connect_timeout=timeout,
88
- pool_timeout=timeout,
89
- )
85
+ try:
86
+ sticker_file = await sticker.get_file(
87
+ read_timeout=timeout,
88
+ write_timeout=timeout,
89
+ connect_timeout=timeout,
90
+ pool_timeout=timeout,
91
+ )
92
+ except TelegramError as e:
93
+ self.cb.put(f"Failed to download {f_id}: {str(e)}")
94
+ results[f_id] = False
95
+ return
90
96
  fpath = sticker_file.file_path
91
97
  assert fpath is not None
92
98
  ext = Path(fpath).suffix
@@ -102,21 +108,25 @@ class DownloadTelegram(DownloadBase):
102
108
  if isinstance(sticker, Sticker) and sticker.emoji is not None:
103
109
  self.emoji_dict[f_id] = sticker.emoji
104
110
  self.cb.put(f"Downloaded {f_name}")
111
+ results[f_id] = True
105
112
  if f_id != "cover":
106
113
  self.cb.put("update_bar")
107
114
 
108
115
  async with anyio.create_task_group() as tg:
109
116
  for num, sticker in enumerate(sticker_set.stickers):
110
117
  f_id = str(num).zfill(3)
111
- tg.start_soon(download_sticker, sticker, f_id)
118
+ tg.start_soon(download_sticker, sticker, f_id, results)
112
119
 
113
120
  if sticker_set.thumbnail is not None:
114
- tg.start_soon(download_sticker, sticker_set.thumbnail, "cover")
121
+ results_thumb: dict[str, bool] = {}
122
+ tg.start_soon(
123
+ download_sticker, sticker_set.thumbnail, "cover", results_thumb
124
+ )
115
125
 
116
126
  MetadataHandler.set_metadata(
117
127
  self.out_dir, title=self.title, emoji_dict=self.emoji_dict
118
128
  )
119
- return True
129
+ return sum(results.values()), len(sticker_set.stickers)
120
130
 
121
131
  @staticmethod
122
132
  def start(
@@ -124,6 +134,6 @@ class DownloadTelegram(DownloadBase):
124
134
  opt_cred: Optional[CredOption],
125
135
  cb: CallbackProtocol,
126
136
  cb_return: CallbackReturn,
127
- ) -> bool:
137
+ ) -> Tuple[int, int]:
128
138
  downloader = DownloadTelegram(opt_input, opt_cred, cb, cb_return)
129
139
  return downloader.download_stickers_telegram()
@@ -44,7 +44,7 @@ class DownloadViber(DownloadBase):
44
44
 
45
45
  return title, zip_url
46
46
 
47
- def decompress(self, zip_file: bytes) -> None:
47
+ def decompress(self, zip_file: bytes) -> int:
48
48
  with zipfile.ZipFile(BytesIO(zip_file)) as zf:
49
49
  self.cb.put("Unzipping...")
50
50
 
@@ -69,19 +69,21 @@ class DownloadViber(DownloadBase):
69
69
 
70
70
  self.cb.put("update_bar")
71
71
 
72
- def download_stickers_viber(self) -> bool:
72
+ return len(zf_files)
73
+
74
+ def download_stickers_viber(self) -> Tuple[int, int]:
73
75
  pack_info = self.get_pack_info(self.url)
74
76
  if pack_info is None:
75
77
  self.cb.put("Download failed: Cannot get pack info")
76
- return False
78
+ return 0, 0
77
79
  title, zip_url = pack_info
78
80
 
79
81
  zip_file = self.download_file(zip_url)
80
- self.decompress(zip_file)
82
+ count = self.decompress(zip_file)
81
83
 
82
84
  MetadataHandler.set_metadata(self.out_dir, title=title)
83
85
 
84
- return True
86
+ return count, count
85
87
 
86
88
  @staticmethod
87
89
  def start(
@@ -89,6 +91,6 @@ class DownloadViber(DownloadBase):
89
91
  opt_cred: Optional[CredOption],
90
92
  cb: CallbackProtocol,
91
93
  cb_return: CallbackReturn,
92
- ) -> bool:
94
+ ) -> Tuple[int, int]:
93
95
  downloader = DownloadViber(opt_input, opt_cred, cb, cb_return)
94
96
  return downloader.download_stickers_viber()