sticker-convert 2.10.8__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.
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"