sticker-convert 2.10.8__py3-none-any.whl → 2.10.9__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.
sticker_convert/job.py CHANGED
@@ -216,7 +216,6 @@ class Job:
216
216
  self.cb_ask_bool = cb_ask_bool
217
217
  self.cb_ask_str = cb_ask_str
218
218
 
219
- self.compress_fails: List[str] = []
220
219
  self.out_urls: List[str] = []
221
220
 
222
221
  self.executor = Executor(
@@ -236,19 +235,21 @@ class Job:
236
235
 
237
236
  self.executor.cb("msg", kwargs={"cls": True})
238
237
 
239
- tasks = (
238
+ tasks: Tuple[Callable[..., Tuple[bool, Optional[str]]], ...] = (
240
239
  self.verify_input,
241
240
  self.cleanup,
242
241
  self.download,
243
242
  self.compress,
244
243
  self.export,
245
- self.report,
246
244
  )
247
245
 
248
246
  code = 0
247
+ summaries: List[str] = []
249
248
  for task in tasks:
250
249
  self.executor.cb("bar", kwargs={"set_progress_mode": "indeterminate"})
251
- success = task()
250
+ success, summary = task()
251
+ if summary is not None:
252
+ summaries.append(summary)
252
253
 
253
254
  if self.executor.is_cancel_job.value == 1: # type: ignore
254
255
  code = 2
@@ -258,6 +259,22 @@ class Job:
258
259
  self.executor.cb("An error occured during this run.")
259
260
  break
260
261
 
262
+ msg = "##########\n"
263
+ msg += "Summary:\n"
264
+ msg += "##########\n"
265
+
266
+ msg += "\n"
267
+ msg += "\n".join(summaries)
268
+ msg += "\n"
269
+
270
+ if self.out_urls:
271
+ msg += "Export results:\n"
272
+ msg += "\n".join(self.out_urls)
273
+ else:
274
+ msg += "Export result: None"
275
+
276
+ self.executor.cb(msg)
277
+
261
278
  self.executor.cleanup()
262
279
 
263
280
  return code
@@ -265,7 +282,7 @@ class Job:
265
282
  def cancel(self, *_: Any, **_kwargs: Any) -> None:
266
283
  self.executor.kill_workers()
267
284
 
268
- def verify_input(self) -> bool:
285
+ def verify_input(self) -> Tuple[bool, None]:
269
286
  info_msg = ""
270
287
  error_msg = ""
271
288
 
@@ -352,7 +369,7 @@ class Job:
352
369
 
353
370
  if error_msg != "":
354
371
  self.executor.cb(error_msg)
355
- return False
372
+ return False, None
356
373
 
357
374
  # Check if preset not equal to export option
358
375
  # Only warn if the compression option is available in export preset
@@ -372,7 +389,7 @@ class Job:
372
389
  response = self.executor.cb_return.get_response()
373
390
 
374
391
  if response is False:
375
- return False
392
+ return False, None
376
393
 
377
394
  for param, value in (
378
395
  ("fps_power", self.opt_comp.fps_power),
@@ -431,7 +448,7 @@ class Job:
431
448
  response = self.executor.cb_return.get_response()
432
449
 
433
450
  if response is False:
434
- return False
451
+ return False, None
435
452
 
436
453
  # Warn about in/output directories that might contain other files
437
454
  # Directory is safe if the name is stickers_input/stickers_output, or
@@ -468,13 +485,13 @@ class Job:
468
485
  response = self.executor.cb_return.get_response()
469
486
 
470
487
  if response is False:
471
- return False
488
+ return False, None
472
489
 
473
490
  break
474
491
 
475
- return True
492
+ return True, None
476
493
 
477
- def cleanup(self) -> bool:
494
+ def cleanup(self) -> Tuple[bool, None]:
478
495
  # If input is 'From local directory', then we should keep files in input/output directory as it maybe edited by user
479
496
  # If input is not 'From local directory', then we should move files in input/output directory as new files will be downloaded
480
497
  # Output directory should be cleanup unless no_compress is true (meaning files in output directory might be edited by user)
@@ -523,10 +540,10 @@ class Job:
523
540
  new_path = Path(archive_dir, old_path.name)
524
541
  old_path.rename(new_path)
525
542
 
526
- return True
543
+ return True, None
527
544
 
528
- def download(self) -> bool:
529
- downloaders: List[Callable[..., bool]] = []
545
+ def download(self) -> Tuple[bool, str]:
546
+ downloaders: List[Callable[..., Tuple[int, int]]] = []
530
547
 
531
548
  if self.opt_input.option == "signal":
532
549
  downloaders.append(DownloadSignal.start)
@@ -549,8 +566,8 @@ class Job:
549
566
  if len(downloaders) > 0:
550
567
  self.executor.cb("Downloading...")
551
568
  else:
552
- self.executor.cb("Nothing to download")
553
- return True
569
+ self.executor.cb("Skipped download (No files to download)")
570
+ return True, "Download: Skipped (No files to download)"
554
571
 
555
572
  self.executor.start_workers(processes=1)
556
573
 
@@ -563,15 +580,24 @@ class Job:
563
580
  self.executor.join_workers()
564
581
 
565
582
  # Return False if any of the job returns failure
583
+ stickers_ok = 0
584
+ stickers_total = 0
585
+ success = True
566
586
  for result in self.executor.results_list:
567
- if result is False:
568
- return False
587
+ stickers_ok += result[0]
588
+ stickers_total += result[1]
589
+ success = (
590
+ success if stickers_ok == stickers_total and stickers_ok > 0 else False
591
+ )
569
592
 
570
- return True
593
+ return (
594
+ success,
595
+ f"Download: {stickers_ok}/{stickers_total} stickers success",
596
+ )
571
597
 
572
- def compress(self) -> bool:
598
+ def compress(self) -> Tuple[bool, str]:
573
599
  if self.opt_comp.no_compress is True:
574
- self.executor.cb("no_compress is set to True, skip compression")
600
+ self.executor.cb("Skipped compression (no_compress is set to True)")
575
601
  in_dir_files = [
576
602
  i
577
603
  for i in sorted(self.opt_input.dir.iterdir())
@@ -598,7 +624,7 @@ class Job:
598
624
  src_f = Path(self.opt_input.dir, i.name)
599
625
  dst_f = Path(self.opt_output.dir, i.name)
600
626
  shutil.copy(src_f, dst_f)
601
- return True
627
+ return True, "Compress: Skipped (no_compress is set to True)"
602
628
  msg = "Compressing..."
603
629
 
604
630
  input_dir = Path(self.opt_input.dir)
@@ -620,8 +646,8 @@ class Job:
620
646
 
621
647
  in_fs_count = len(in_fs)
622
648
  if in_fs_count == 0:
623
- self.executor.cb("No files to compress")
624
- return True
649
+ self.executor.cb("Skipped compression (No files to compress)")
650
+ return True, "Compress: Skipped (No files to compress)"
625
651
 
626
652
  self.executor.cb(msg)
627
653
  self.executor.cb(
@@ -640,21 +666,35 @@ class Job:
640
666
 
641
667
  self.executor.join_workers()
642
668
 
643
- # Return False if any of the job returns failure
669
+ success = True
670
+ stickers_ok = 0
671
+ stickers_total = 0
672
+ fails: List[str] = []
644
673
  for result in self.executor.results_list:
674
+ stickers_total += 1
645
675
  if result[0] is False:
646
- return False
676
+ success = False
677
+ fails.append(str(result[1]))
678
+ else:
679
+ stickers_ok += 1
680
+
681
+ msg_append = ""
682
+ if success is False:
683
+ msg_append = " (Failed: " + ", ".join(fails) + ")"
647
684
 
648
- return True
685
+ return (
686
+ success,
687
+ f"Compress: {stickers_ok}/{stickers_total} stickers success" + msg_append,
688
+ )
649
689
 
650
- def export(self) -> bool:
690
+ def export(self) -> Tuple[bool, str]:
651
691
  if self.opt_output.option == "local":
652
- self.executor.cb("Saving to local directory only, nothing to export")
653
- return True
692
+ self.executor.cb("Skipped export (Saving to local directory only)")
693
+ return True, "Export: Skipped (Saving to local directory only)"
654
694
 
655
695
  self.executor.cb("Exporting...")
656
696
 
657
- exporters: List[Callable[..., List[str]]] = []
697
+ exporters: List[Callable[..., Tuple[int, int, List[str]]]] = []
658
698
 
659
699
  if self.opt_output.option == "whatsapp":
660
700
  exporters.append(CompressWastickers.start)
@@ -684,8 +724,12 @@ class Job:
684
724
 
685
725
  self.executor.join_workers()
686
726
 
727
+ stickers_ok = 0
728
+ stickers_total = 0
687
729
  for result in self.executor.results_list:
688
- self.out_urls.extend(result)
730
+ stickers_ok += result[0]
731
+ stickers_total += result[1]
732
+ self.out_urls.extend(result[2])
689
733
 
690
734
  if self.out_urls:
691
735
  with open(
@@ -694,29 +738,6 @@ class Job:
694
738
  f.write("\n".join(self.out_urls))
695
739
  else:
696
740
  self.executor.cb("An error occured while exporting stickers")
697
- return False
698
-
699
- return True
700
-
701
- def report(self) -> bool:
702
- msg = "##########\n"
703
- msg += "Summary:\n"
704
- msg += "##########\n"
705
- msg += "\n"
706
-
707
- if self.compress_fails:
708
- msg += f'Warning: Could not compress the following {len(self.compress_fails)} file{"s" if len(self.compress_fails) > 1 else ""}:\n'
709
- msg += "\n".join(self.compress_fails)
710
- msg += "\n"
711
- msg += "\nConsider adjusting compression parameters"
712
- msg += "\n"
713
-
714
- if self.out_urls:
715
- msg += "Export results:\n"
716
- msg += "\n".join(self.out_urls)
717
- else:
718
- msg += "Export result: None"
719
-
720
- self.executor.cb(msg)
741
+ return False, f"Export: {stickers_ok}/{stickers_total} stickers success"
721
742
 
722
- return True
743
+ return True, f"Export: {stickers_ok}/{stickers_total} stickers success"
@@ -3,7 +3,7 @@ import copy
3
3
  import shutil
4
4
  import zipfile
5
5
  from pathlib import Path
6
- from typing import Any, List
6
+ from typing import Any, List, Tuple
7
7
 
8
8
  from sticker_convert.converter import StickerConvert
9
9
  from sticker_convert.job_option import CompOption, CredOption, OutputOption
@@ -47,7 +47,7 @@ class CompressWastickers(UploadBase):
47
47
  self.opt_comp_merged = copy.deepcopy(self.opt_comp)
48
48
  self.opt_comp_merged.merge(self.base_spec)
49
49
 
50
- def compress_wastickers(self) -> List[str]:
50
+ def compress_wastickers(self) -> Tuple[int, int, List[str]]:
51
51
  urls: List[str] = []
52
52
  title, author, _ = MetadataHandler.get_metadata(
53
53
  self.opt_output.dir,
@@ -56,10 +56,10 @@ class CompressWastickers(UploadBase):
56
56
  )
57
57
  if not title:
58
58
  self.cb.put("Title is required for compressing .wastickers")
59
- return urls
59
+ return 0, 0, urls
60
60
  if not author:
61
61
  self.cb.put("Author is required for compressing .wastickers")
62
- return urls
62
+ return 0, 0, urls
63
63
  packs = MetadataHandler.split_sticker_packs(
64
64
  self.opt_output.dir,
65
65
  title=title,
@@ -67,7 +67,9 @@ class CompressWastickers(UploadBase):
67
67
  separate_image_anim=not self.opt_comp.fake_vid,
68
68
  )
69
69
 
70
+ stickers_total = 0
70
71
  for pack_title, stickers in packs.items():
72
+ stickers_total += len(stickers)
71
73
  # Originally the Sticker Maker application name the files with int(time.time())
72
74
  with CacheStore.get_cache_store(path=self.opt_comp.cache_dir) as tempdir:
73
75
  for num, src in enumerate(stickers):
@@ -106,7 +108,7 @@ class CompressWastickers(UploadBase):
106
108
  self.cb.put((out_f))
107
109
  urls.append(out_f)
108
110
 
109
- return urls
111
+ return stickers_total, stickers_total, urls
110
112
 
111
113
  def add_metadata(self, pack_dir: Path, title: str, author: str) -> None:
112
114
  opt_comp_merged = copy.deepcopy(self.opt_comp)
@@ -150,6 +152,6 @@ class CompressWastickers(UploadBase):
150
152
  opt_cred: CredOption,
151
153
  cb: CallbackProtocol,
152
154
  cb_return: CallbackReturn,
153
- ) -> List[str]:
155
+ ) -> Tuple[int, int, List[str]]:
154
156
  exporter = CompressWastickers(opt_output, opt_comp, opt_cred, cb, cb_return)
155
157
  return exporter.compress_wastickers()
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
  import copy
3
3
  from pathlib import Path
4
- from typing import Any, Dict, List
4
+ from typing import Any, Dict, List, Tuple
5
5
 
6
6
  import anyio
7
7
  from signalstickers_client.errors import SignalException
@@ -91,15 +91,15 @@ class UploadSignal(UploadBase):
91
91
 
92
92
  pack._addsticker(sticker) # type: ignore
93
93
 
94
- def upload_stickers_signal(self) -> List[str]:
94
+ def upload_stickers_signal(self) -> Tuple[int, int, List[str]]:
95
95
  urls: List[str] = []
96
96
 
97
97
  if not self.opt_cred.signal_uuid:
98
98
  self.cb.put("uuid required for uploading to Signal")
99
- return urls
99
+ return 0, 0, urls
100
100
  if not self.opt_cred.signal_password:
101
101
  self.cb.put("password required for uploading to Signal")
102
- return urls
102
+ return 0, 0, urls
103
103
 
104
104
  title, author, emoji_dict = MetadataHandler.get_metadata(
105
105
  self.opt_output.dir,
@@ -138,7 +138,10 @@ class UploadSignal(UploadBase):
138
138
  file_per_pack=200,
139
139
  separate_image_anim=False,
140
140
  )
141
+ stickers_total = 0
142
+ stickers_ok = 0
141
143
  for pack_title, stickers in packs.items():
144
+ stickers_total += len(stickers)
142
145
  pack = LocalStickerPack()
143
146
  pack.title = pack_title
144
147
  pack.author = author
@@ -155,11 +158,12 @@ class UploadSignal(UploadBase):
155
158
  )
156
159
  self.cb.put((result))
157
160
  urls.append(result)
161
+ stickers_ok += len(stickers)
158
162
 
159
163
  except SignalException as e:
160
164
  self.cb.put(f"Failed to upload pack {pack_title} due to {repr(e)}")
161
165
 
162
- return urls
166
+ return stickers_ok, stickers_total, urls
163
167
 
164
168
  @staticmethod
165
169
  def start(
@@ -168,6 +172,6 @@ class UploadSignal(UploadBase):
168
172
  opt_cred: CredOption,
169
173
  cb: CallbackProtocol,
170
174
  cb_return: CallbackReturn,
171
- ) -> List[str]:
175
+ ) -> Tuple[int, int, List[str]]:
172
176
  exporter = UploadSignal(opt_output, opt_comp, opt_cred, cb, cb_return)
173
177
  return exporter.upload_stickers_signal()
@@ -2,7 +2,7 @@
2
2
  import copy
3
3
  import re
4
4
  from pathlib import Path
5
- from typing import Any, Dict, List, Optional, Union, cast
5
+ from typing import Any, Dict, List, Optional, Tuple, Union, cast
6
6
 
7
7
  import anyio
8
8
  from telegram import InputSticker, Sticker
@@ -153,6 +153,7 @@ class UploadTelegram(UploadBase):
153
153
  sticker_type = Sticker.REGULAR
154
154
 
155
155
  init_input_stickers: List[InputSticker] = []
156
+ extra_input_stickers: List[Tuple[InputSticker, Path]] = []
156
157
  sticker_format = None
157
158
  for src in stickers:
158
159
  self.cb.put(f"Verifying {src} for uploading to telegram")
@@ -204,36 +205,15 @@ class UploadTelegram(UploadBase):
204
205
  format=sticker_format,
205
206
  )
206
207
 
207
- if sticker_set is None:
208
+ if sticker_set is None and len(init_input_stickers) < 50:
208
209
  init_input_stickers.append(input_sticker)
209
210
  else:
210
- try:
211
- # We could use tg.start_soon() here
212
- # But this would disrupt the order of stickers
213
- await bot.add_sticker_to_set(
214
- user_id=self.telegram_userid,
215
- name=pack_short_name,
216
- sticker=input_sticker,
217
- )
218
- self.cb.put(f"Uploaded sticker {src} of {pack_short_name}")
219
- except BadRequest as e:
220
- self.cb.put(
221
- f"Cannot upload sticker {src} of {pack_short_name} due to {e}"
222
- )
223
- if str(e) == "Stickerpack_not_found":
224
- self.cb.put(
225
- "Hint: You might had deleted and recreated pack too quickly. Wait about 3 minutes and try again."
226
- )
227
- except TelegramError as e:
228
- self.cb.put(
229
- f"Cannot upload sticker {src} of {pack_short_name} due to {e}"
230
- )
211
+ extra_input_stickers.append((input_sticker, src))
231
212
 
232
- if sticker_set is None and len(init_input_stickers) > 0:
233
- start_msg = f"Creating pack and bulk uploading {len(init_input_stickers)} stickers with same format of {pack_short_name}"
234
- finish_msg = f"Created pack and bulk uploaded {len(init_input_stickers)} stickers with same format of {pack_short_name}"
235
- error_msg = f"Cannot create pack and bulk upload {len(init_input_stickers)} stickers with same format of {pack_short_name} due to"
236
- self.cb.put(start_msg)
213
+ if len(init_input_stickers) > 0:
214
+ self.cb.put(
215
+ f"Creating pack and bulk uploading {len(init_input_stickers)} stickers of {pack_short_name}"
216
+ )
237
217
  try:
238
218
  await bot.create_new_sticker_set(
239
219
  user_id=self.telegram_userid,
@@ -243,11 +223,38 @@ class UploadTelegram(UploadBase):
243
223
  sticker_type=sticker_type,
244
224
  )
245
225
  sticker_set = True
246
- self.cb.put(finish_msg)
226
+ self.cb.put(
227
+ f"Created pack and bulk uploaded {len(init_input_stickers)} stickers of {pack_short_name}"
228
+ )
247
229
  except TelegramError as e:
248
- self.cb.put(f"{error_msg} {e}")
230
+ self.cb.put(
231
+ f"Cannot create pack and bulk upload {len(init_input_stickers)} stickers of {pack_short_name} due to {e}"
232
+ )
249
233
  return None
250
234
 
235
+ for input_sticker, src in extra_input_stickers:
236
+ try:
237
+ # We could use tg.start_soon() here
238
+ # But this would disrupt the order of stickers
239
+ await bot.add_sticker_to_set(
240
+ user_id=self.telegram_userid,
241
+ name=pack_short_name,
242
+ sticker=input_sticker,
243
+ )
244
+ self.cb.put(f"Uploaded sticker {src} of {pack_short_name}")
245
+ except BadRequest as e:
246
+ self.cb.put(
247
+ f"Cannot upload sticker {src} of {pack_short_name} due to {e}"
248
+ )
249
+ if str(e) == "Stickerpack_not_found":
250
+ self.cb.put(
251
+ "Hint: You might had deleted and recreated pack too quickly. Wait about 3 minutes and try again."
252
+ )
253
+ except TelegramError as e:
254
+ self.cb.put(
255
+ f"Cannot upload sticker {src} of {pack_short_name} due to {e}"
256
+ )
257
+
251
258
  cover_path = MetadataHandler.get_cover(self.opt_output.dir)
252
259
  if cover_path:
253
260
  thumbnail_bytes: Union[None, bytes, Path] = None
@@ -300,18 +307,18 @@ class UploadTelegram(UploadBase):
300
307
  result = f"https://t.me/addstickers/{pack_short_name}"
301
308
  return result
302
309
 
303
- def upload_stickers_telegram(self) -> List[str]:
310
+ def upload_stickers_telegram(self) -> Tuple[int, int, List[str]]:
304
311
  urls: List[str] = []
305
312
 
306
313
  if not (self.opt_cred.telegram_token and self.opt_cred.telegram_userid):
307
314
  self.cb.put("Token and userid required for uploading to telegram")
308
- return urls
315
+ return 0, 0, urls
309
316
 
310
317
  if self.opt_cred.telegram_userid.isnumeric():
311
318
  self.telegram_userid = int(self.opt_cred.telegram_userid)
312
319
  else:
313
320
  self.cb.put("Invalid userid, should contain numbers only")
314
- return urls
321
+ return 0, 0, urls
315
322
 
316
323
  title, _, emoji_dict = MetadataHandler.get_metadata(
317
324
  self.opt_output.dir,
@@ -355,14 +362,18 @@ class UploadTelegram(UploadBase):
355
362
  separate_image_anim=not self.opt_comp.fake_vid,
356
363
  )
357
364
 
365
+ stickers_total = 0
366
+ stickers_ok = 0
358
367
  for pack_title, stickers in packs.items():
368
+ stickers_total += len(stickers)
359
369
  self.cb.put(f"Uploading pack {pack_title}")
360
370
  result = anyio.run(self.upload_pack, pack_title, stickers, emoji_dict)
361
371
  if result:
362
372
  self.cb.put((result))
363
373
  urls.append(result)
374
+ stickers_ok += len(stickers)
364
375
 
365
- return urls
376
+ return stickers_ok, stickers_total, urls
366
377
 
367
378
  @staticmethod
368
379
  def start(
@@ -371,7 +382,7 @@ class UploadTelegram(UploadBase):
371
382
  opt_cred: CredOption,
372
383
  cb: CallbackProtocol,
373
384
  cb_return: CallbackReturn,
374
- ) -> List[str]:
385
+ ) -> Tuple[int, int, List[str]]:
375
386
  exporter = UploadTelegram(
376
387
  opt_output,
377
388
  opt_comp,
@@ -4,7 +4,7 @@ import json
4
4
  import shutil
5
5
  import zipfile
6
6
  from pathlib import Path
7
- from typing import Any, Dict, List
7
+ from typing import Any, Dict, List, Tuple
8
8
 
9
9
  import requests
10
10
 
@@ -35,12 +35,12 @@ class UploadViber(UploadBase):
35
35
  self.opt_comp_merged = copy.deepcopy(self.opt_comp)
36
36
  self.opt_comp_merged.merge(self.png_spec)
37
37
 
38
- def upload_stickers_viber(self) -> List[str]:
38
+ def upload_stickers_viber(self) -> Tuple[int, int, List[str]]:
39
39
  urls: List[str] = []
40
40
 
41
41
  if not self.opt_cred.viber_auth:
42
42
  self.cb.put("Viber auth required for uploading to viber")
43
- return urls
43
+ return 0, 0, urls
44
44
 
45
45
  upload_data_base: Dict[str, str] = {}
46
46
  for i in self.opt_cred.viber_auth.split(";"):
@@ -49,13 +49,13 @@ class UploadViber(UploadBase):
49
49
 
50
50
  if upload_data_base.get("member_id") is None:
51
51
  self.cb.put("Invalid Viber auth: Missing member_id")
52
- return urls
52
+ return 0, 0, urls
53
53
  if upload_data_base.get("m_token") is None:
54
54
  self.cb.put("Invalid Viber auth: Missing m_token")
55
- return urls
55
+ return 0, 0, urls
56
56
  if upload_data_base.get("m_ts") is None:
57
57
  self.cb.put("Invalid Viber auth: Missing m_ts")
58
- return urls
58
+ return 0, 0, urls
59
59
 
60
60
  title, author, _ = MetadataHandler.get_metadata(
61
61
  self.opt_output.dir,
@@ -92,7 +92,10 @@ class UploadViber(UploadBase):
92
92
  self.cb_return,
93
93
  )
94
94
 
95
+ stickers_total = 0
96
+ stickers_ok = 0
95
97
  for pack_title, stickers in packs.items():
98
+ stickers_total += len(stickers)
96
99
  with CacheStore.get_cache_store(path=self.opt_comp.cache_dir) as tempdir:
97
100
  for num, src in enumerate(stickers):
98
101
  self.cb.put(f"Verifying {src} for uploading to Viber")
@@ -141,6 +144,7 @@ class UploadViber(UploadBase):
141
144
  url = f"https://stickers.viber.com/pages/custom-sticker-packs/{pack_id}"
142
145
  urls.append(url)
143
146
  self.cb.put(f"Uploaded {pack_title}")
147
+ stickers_ok += len(stickers)
144
148
  else:
145
149
  self.cb.put(
146
150
  f"Failed to upload {pack_title}: {r.status_code} {r.text}"
@@ -152,7 +156,7 @@ class UploadViber(UploadBase):
152
156
  else:
153
157
  self.cb.put(f"Failed to upload {pack_title}: {r.status_code} {r.text}")
154
158
 
155
- return urls
159
+ return stickers_ok, stickers_total, urls
156
160
 
157
161
  @staticmethod
158
162
  def start(
@@ -161,6 +165,6 @@ class UploadViber(UploadBase):
161
165
  opt_cred: CredOption,
162
166
  cb: CallbackProtocol,
163
167
  cb_return: CallbackReturn,
164
- ) -> List[str]:
168
+ ) -> Tuple[int, int, List[str]]:
165
169
  exporter = UploadViber(opt_output, opt_comp, opt_cred, cb, cb_return)
166
170
  return exporter.upload_stickers_viber()
@@ -6,7 +6,7 @@ import plistlib
6
6
  import shutil
7
7
  import zipfile
8
8
  from pathlib import Path
9
- from typing import Any, Dict, List
9
+ from typing import Any, Dict, List, Tuple
10
10
 
11
11
  from sticker_convert.converter import StickerConvert
12
12
  from sticker_convert.definitions import ROOT_DIR
@@ -34,7 +34,7 @@ class XcodeImessage(UploadBase):
34
34
  self.large_spec = copy.deepcopy(self.base_spec)
35
35
  self.large_spec.set_res(618)
36
36
 
37
- def create_imessage_xcode(self) -> List[str]:
37
+ def create_imessage_xcode(self) -> Tuple[int, int, List[str]]:
38
38
  urls: List[str] = []
39
39
  title, author, _ = MetadataHandler.get_metadata(
40
40
  self.opt_output.dir,
@@ -43,10 +43,10 @@ class XcodeImessage(UploadBase):
43
43
  )
44
44
  if not author:
45
45
  self.cb.put("author is required for creating Xcode iMessage sticker pack")
46
- return urls
46
+ return 0, 0, urls
47
47
  if not title:
48
48
  self.cb.put("title is required for creating Xcode iMessage sticker pack")
49
- return urls
49
+ return 0, 0, urls
50
50
 
51
51
  author = author.replace(" ", "_")
52
52
  title = title.replace(" ", "_")
@@ -61,6 +61,7 @@ class XcodeImessage(UploadBase):
61
61
  spec_choice = None
62
62
  opt_comp_merged = copy.deepcopy(self.opt_comp)
63
63
 
64
+ stickers_total = 0
64
65
  for pack_title, stickers in packs.items():
65
66
  pack_title = sanitize_filename(pack_title)
66
67
 
@@ -87,6 +88,7 @@ class XcodeImessage(UploadBase):
87
88
  StickerConvert.convert(
88
89
  fpath, fpath, opt_comp_merged, self.cb, self.cb_return
89
90
  )
91
+ stickers_total += 1
90
92
 
91
93
  self.add_metadata(author, pack_title)
92
94
  self.create_xcode_proj(author, pack_title)
@@ -95,7 +97,7 @@ class XcodeImessage(UploadBase):
95
97
  self.cb.put(result)
96
98
  urls.append(result)
97
99
 
98
- return urls
100
+ return stickers_total, stickers_total, urls
99
101
 
100
102
  def add_metadata(self, author: str, title: str) -> None:
101
103
  first_image_path = Path(
@@ -279,6 +281,6 @@ class XcodeImessage(UploadBase):
279
281
  opt_cred: CredOption,
280
282
  cb: CallbackProtocol,
281
283
  cb_return: CallbackReturn,
282
- ) -> List[str]:
284
+ ) -> Tuple[int, int, List[str]]:
283
285
  exporter = XcodeImessage(opt_output, opt_comp, opt_cred, cb, cb_return)
284
286
  return exporter.create_imessage_xcode()
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
- __version__ = "2.10.8"
3
+ __version__ = "2.10.9"