sticker-convert 2.7.3__py3-none-any.whl → 2.7.4__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.
Files changed (52) hide show
  1. sticker_convert/cli.py +12 -16
  2. sticker_convert/converter.py +46 -50
  3. sticker_convert/definitions.py +8 -12
  4. sticker_convert/downloaders/download_base.py +13 -30
  5. sticker_convert/downloaders/download_kakao.py +19 -25
  6. sticker_convert/downloaders/download_line.py +7 -8
  7. sticker_convert/downloaders/download_signal.py +4 -5
  8. sticker_convert/downloaders/download_telegram.py +4 -5
  9. sticker_convert/gui.py +28 -29
  10. sticker_convert/gui_components/frames/comp_frame.py +7 -7
  11. sticker_convert/gui_components/frames/config_frame.py +7 -7
  12. sticker_convert/gui_components/frames/control_frame.py +1 -1
  13. sticker_convert/gui_components/frames/cred_frame.py +8 -8
  14. sticker_convert/gui_components/frames/input_frame.py +6 -6
  15. sticker_convert/gui_components/frames/output_frame.py +6 -6
  16. sticker_convert/gui_components/frames/progress_frame.py +6 -6
  17. sticker_convert/gui_components/gui_utils.py +2 -4
  18. sticker_convert/gui_components/windows/advanced_compression_window.py +13 -11
  19. sticker_convert/gui_components/windows/base_window.py +3 -3
  20. sticker_convert/gui_components/windows/kakao_get_auth_window.py +2 -2
  21. sticker_convert/gui_components/windows/line_get_auth_window.py +2 -2
  22. sticker_convert/gui_components/windows/signal_get_auth_window.py +1 -1
  23. sticker_convert/job.py +66 -75
  24. sticker_convert/job_option.py +1 -1
  25. sticker_convert/resources/help.json +1 -1
  26. sticker_convert/uploaders/compress_wastickers.py +3 -3
  27. sticker_convert/uploaders/upload_base.py +2 -3
  28. sticker_convert/uploaders/upload_signal.py +5 -5
  29. sticker_convert/uploaders/upload_telegram.py +5 -4
  30. sticker_convert/uploaders/xcode_imessage.py +14 -69
  31. sticker_convert/utils/auth/get_kakao_auth.py +4 -5
  32. sticker_convert/utils/auth/get_line_auth.py +1 -2
  33. sticker_convert/utils/auth/get_signal_auth.py +4 -4
  34. sticker_convert/utils/callback.py +25 -17
  35. sticker_convert/utils/files/cache_store.py +4 -6
  36. sticker_convert/utils/files/json_manager.py +3 -4
  37. sticker_convert/utils/files/json_resources_loader.py +12 -0
  38. sticker_convert/utils/files/metadata_handler.py +83 -74
  39. sticker_convert/utils/files/run_bin.py +8 -7
  40. sticker_convert/utils/files/sanitize_filename.py +30 -28
  41. sticker_convert/utils/media/apple_png_normalize.py +2 -2
  42. sticker_convert/utils/media/codec_info.py +22 -29
  43. sticker_convert/utils/media/decrypt_kakao.py +3 -5
  44. sticker_convert/utils/media/format_verify.py +6 -8
  45. sticker_convert/utils/url_detect.py +4 -5
  46. sticker_convert/version.py +1 -1
  47. {sticker_convert-2.7.3.dist-info → sticker_convert-2.7.4.dist-info}/METADATA +16 -14
  48. {sticker_convert-2.7.3.dist-info → sticker_convert-2.7.4.dist-info}/RECORD +52 -51
  49. {sticker_convert-2.7.3.dist-info → sticker_convert-2.7.4.dist-info}/WHEEL +1 -1
  50. {sticker_convert-2.7.3.dist-info → sticker_convert-2.7.4.dist-info}/LICENSE +0 -0
  51. {sticker_convert-2.7.3.dist-info → sticker_convert-2.7.4.dist-info}/entry_points.txt +0 -0
  52. {sticker_convert-2.7.3.dist-info → sticker_convert-2.7.4.dist-info}/top_level.txt +0 -0
sticker_convert/job.py CHANGED
@@ -7,15 +7,14 @@ import shutil
7
7
  import traceback
8
8
  from datetime import datetime
9
9
  from multiprocessing import Process, Value
10
- from multiprocessing.managers import SyncManager
10
+ from multiprocessing.managers import ListProxy, SyncManager
11
11
  from pathlib import Path
12
12
  from queue import Queue
13
13
  from threading import Thread
14
- from typing import Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple, Union
14
+ from typing import TYPE_CHECKING, Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple
15
15
  from urllib.parse import urlparse
16
16
 
17
17
  from sticker_convert.converter import StickerConvert
18
- from sticker_convert.definitions import ROOT_DIR
19
18
  from sticker_convert.downloaders.download_kakao import DownloadKakao
20
19
  from sticker_convert.downloaders.download_line import DownloadLine
21
20
  from sticker_convert.downloaders.download_signal import DownloadSignal
@@ -25,11 +24,19 @@ from sticker_convert.uploaders.compress_wastickers import CompressWastickers
25
24
  from sticker_convert.uploaders.upload_signal import UploadSignal
26
25
  from sticker_convert.uploaders.upload_telegram import UploadTelegram
27
26
  from sticker_convert.uploaders.xcode_imessage import XcodeImessage
28
- from sticker_convert.utils.callback import CallbackReturn
29
- from sticker_convert.utils.files.json_manager import JsonManager
27
+ from sticker_convert.utils.callback import CallbackReturn, CbQueueItemType
28
+ from sticker_convert.utils.files.json_resources_loader import OUTPUT_JSON
30
29
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
31
30
  from sticker_convert.utils.media.codec_info import CodecInfo
32
31
 
32
+ CbQueueType = Queue[CbQueueItemType]
33
+ WorkListItemType = Optional[Tuple[Callable[..., Any], Tuple[Any, ...]]]
34
+ if TYPE_CHECKING:
35
+ # mypy complains about this
36
+ WorkListType = ListProxy[WorkListItemType] # type: ignore
37
+ else:
38
+ WorkListType = List[WorkListItemType]
39
+
33
40
 
34
41
  class Executor:
35
42
  def __init__(
@@ -39,7 +46,7 @@ class Executor:
39
46
  cb_bar: Callable[..., None],
40
47
  cb_ask_bool: Callable[..., bool],
41
48
  cb_ask_str: Callable[..., str],
42
- ):
49
+ ) -> None:
43
50
  self.cb_msg = cb_msg
44
51
  self.cb_msg_block = cb_msg_block
45
52
  self.cb_bar = cb_bar
@@ -48,19 +55,11 @@ class Executor:
48
55
 
49
56
  self.manager = SyncManager()
50
57
  self.manager.start()
51
- self.work_queue: Queue[Optional[Tuple[Callable[..., Any], Tuple[Any, ...]]]] = (
52
- self.manager.Queue()
53
- )
58
+ # Using list instead of queue for work_list as it can cause random deadlocks
59
+ # Especially when using scale_filter=nearest
60
+ self.work_list: WorkListType = self.manager.list()
54
61
  self.results_queue: Queue[Any] = self.manager.Queue()
55
- self.cb_queue: Queue[
56
- Union[
57
- Tuple[
58
- Optional[str], Optional[Tuple[Any, ...]], Optional[Dict[str, str]]
59
- ],
60
- str,
61
- None,
62
- ]
63
- ] = self.manager.Queue()
62
+ self.cb_queue: CbQueueType = self.manager.Queue()
64
63
  self.cb_return = CallbackReturn()
65
64
  self.processes: List[Process] = []
66
65
 
@@ -77,16 +76,9 @@ class Executor:
77
76
 
78
77
  def cb_thread(
79
78
  self,
80
- cb_queue: Queue[
81
- Union[
82
- Tuple[
83
- Optional[str], Optional[Tuple[Any, ...]], Optional[Dict[str, str]]
84
- ],
85
- str,
86
- ]
87
- ],
79
+ cb_queue: CbQueueType,
88
80
  cb_return: CallbackReturn,
89
- ):
81
+ ) -> None:
90
82
  for i in iter(cb_queue.get, None):
91
83
  if isinstance(i, tuple):
92
84
  action = i[0]
@@ -95,13 +87,13 @@ class Executor:
95
87
  else:
96
88
  args = tuple()
97
89
  if len(i) >= 3:
98
- kwargs: Dict[str, str] = i[2] if i[2] else dict()
90
+ kwargs: Dict[str, str] = i[2] if i[2] else {}
99
91
  else:
100
- kwargs = dict()
92
+ kwargs = {}
101
93
  else:
102
94
  action = i
103
95
  args = tuple()
104
- kwargs = dict()
96
+ kwargs = {}
105
97
  if action == "msg":
106
98
  self.cb_msg(*args, **kwargs)
107
99
  elif action == "bar":
@@ -119,19 +111,22 @@ class Executor:
119
111
 
120
112
  @staticmethod
121
113
  def worker(
122
- work_queue: Queue[Optional[Tuple[Callable[..., Any], Tuple[Any, ...]]]],
114
+ work_list: WorkListType,
123
115
  results_queue: Queue[Any],
124
- cb_queue: Queue[
125
- Union[
126
- Tuple[
127
- Optional[str], Optional[Tuple[Any, ...]], Optional[Dict[str, str]]
128
- ],
129
- str,
130
- ]
131
- ],
116
+ cb_queue: CbQueueType,
132
117
  cb_return: CallbackReturn,
133
- ):
134
- for work_func, work_args in iter(work_queue.get, None):
118
+ ) -> None:
119
+ while True:
120
+ try:
121
+ work = work_list.pop(0)
122
+ except IndexError:
123
+ break
124
+
125
+ if work is None:
126
+ break
127
+ else:
128
+ work_func, work_args = work
129
+
135
130
  try:
136
131
  results = work_func(*work_args, cb_queue, cb_return)
137
132
  results_queue.put(results)
@@ -148,18 +143,14 @@ class Executor:
148
143
  e += traceback.format_exc()
149
144
  e += "#####################"
150
145
  cb_queue.put(e)
151
- work_queue.put(None)
146
+ work_list.append(None)
152
147
 
153
148
  def start_workers(self, processes: int = 1) -> None:
154
- # Would contain None from previous run
155
- while not self.work_queue.empty():
156
- self.work_queue.get()
157
-
158
149
  for _ in range(processes):
159
150
  process = Process(
160
151
  target=Executor.worker,
161
152
  args=(
162
- self.work_queue,
153
+ self.work_list,
163
154
  self.results_queue,
164
155
  self.cb_queue,
165
156
  self.cb_return,
@@ -173,10 +164,10 @@ class Executor:
173
164
  def add_work(
174
165
  self, work_func: Callable[..., Any], work_args: Tuple[Any, ...]
175
166
  ) -> None:
176
- self.work_queue.put((work_func, work_args))
167
+ self.work_list.append((work_func, work_args))
177
168
 
178
169
  def join_workers(self) -> None:
179
- self.work_queue.put(None)
170
+ self.work_list.append(None)
180
171
  try:
181
172
  for process in self.processes:
182
173
  process.join()
@@ -184,13 +175,12 @@ class Executor:
184
175
  pass
185
176
 
186
177
  self.results_queue.put(None)
187
-
178
+ self.work_list = self.manager.list()
188
179
  self.processes.clear()
189
180
 
190
- def kill_workers(self, *args: Any, **kwargs: Any) -> None:
181
+ def kill_workers(self, *_: Any, **__: Any) -> None:
191
182
  self.is_cancel_job.value = 1 # type: ignore
192
- while not self.work_queue.empty():
193
- self.work_queue.get()
183
+ self.work_list = self.manager.list()
194
184
 
195
185
  for process in self.processes:
196
186
  if platform.system() == "Windows":
@@ -204,18 +194,18 @@ class Executor:
204
194
  def cleanup(self) -> None:
205
195
  self.cb_queue.put(None)
206
196
  self.cb_thread_instance.join()
197
+ self.work_list = self.manager.list()
207
198
 
208
199
  def get_result(self) -> Generator[Any, None, None]:
209
200
  gen: Iterator[Any] = iter(self.results_queue.get, None)
210
- for result in gen:
211
- yield result
201
+ yield from gen
212
202
 
213
203
  def cb(
214
204
  self,
215
205
  action: Optional[str],
216
206
  args: Optional[Tuple[str, ...]] = None,
217
207
  kwargs: Optional[Dict[str, Any]] = None,
218
- ):
208
+ ) -> None:
219
209
  self.cb_queue.put((action, args, kwargs))
220
210
 
221
211
 
@@ -231,7 +221,7 @@ class Job:
231
221
  cb_bar: Callable[..., None],
232
222
  cb_ask_bool: Callable[..., bool],
233
223
  cb_ask_str: Callable[..., str],
234
- ):
224
+ ) -> None:
235
225
  self.opt_input = opt_input
236
226
  self.opt_comp = opt_comp
237
227
  self.opt_output = opt_output
@@ -280,7 +270,7 @@ class Job:
280
270
  code = 2
281
271
  self.executor.cb("Job cancelled.")
282
272
  break
283
- elif not success:
273
+ if not success:
284
274
  code = 1
285
275
  self.executor.cb("An error occured during this run.")
286
276
  break
@@ -290,7 +280,7 @@ class Job:
290
280
 
291
281
  return code
292
282
 
293
- def cancel(self, *args: Any, **kwargs: Any) -> None:
283
+ def cancel(self, *_: Any, **_kwargs: Any) -> None:
294
284
  self.executor.kill_workers()
295
285
 
296
286
  def verify_input(self) -> bool:
@@ -335,7 +325,7 @@ class Job:
335
325
  error_msg += "[X] Uploading to signal requires uuid and password.\n"
336
326
  error_msg += save_to_local_tip
337
327
 
338
- output_presets = JsonManager.load_json(ROOT_DIR / "resources/output.json")
328
+ output_presets = OUTPUT_JSON
339
329
 
340
330
  input_option = self.opt_input.option
341
331
  output_option = self.opt_output.option
@@ -404,7 +394,9 @@ class Job:
404
394
 
405
395
  if self.opt_comp.scale_filter not in (
406
396
  "nearest",
397
+ "box",
407
398
  "bilinear",
399
+ "hamming",
408
400
  "bicubic",
409
401
  "lanczos",
410
402
  ):
@@ -412,7 +404,9 @@ class Job:
412
404
  error_msg += (
413
405
  f"[X] scale_filter {self.opt_comp.scale_filter} is not valid option"
414
406
  )
415
- error_msg += " Valid options: nearest, bilinear, bicubic, lanczos"
407
+ error_msg += (
408
+ " Valid options: nearest, box, bilinear, hamming, bicubic, lanczos"
409
+ )
416
410
 
417
411
  if self.opt_comp.quantize_method not in ("imagequant", "fastoctree", "none"):
418
412
  error_msg += "\n"
@@ -451,7 +445,7 @@ class Job:
451
445
  or not any(path.iterdir())
452
446
  ):
453
447
  continue
454
- elif path_type == "Output" and (
448
+ if path_type == "Output" and (
455
449
  path.name == "stickers_output"
456
450
  or self.opt_comp.no_compress
457
451
  or not any(path.iterdir())
@@ -459,7 +453,7 @@ class Job:
459
453
  continue
460
454
 
461
455
  related_files = MetadataHandler.get_files_related_to_sticker_convert(path)
462
- if any([i for i in path.iterdir() if i not in related_files]):
456
+ if any(i for i in path.iterdir() if i not in related_files):
463
457
  msg = "WARNING: {} directory is set to {}.\n"
464
458
  msg += 'It does not have default name of "{}",\n'
465
459
  msg += "and It seems like it contains PERSONAL DATA.\n"
@@ -551,14 +545,13 @@ class Job:
551
545
  self.executor.cb("Nothing to download")
552
546
  return True
553
547
 
554
- self.executor.start_workers(processes=1)
555
-
556
548
  for downloader in downloaders:
557
549
  self.executor.add_work(
558
550
  work_func=downloader,
559
551
  work_args=(self.opt_input.url, self.opt_input.dir, self.opt_cred),
560
552
  )
561
553
 
554
+ self.executor.start_workers(processes=1)
562
555
  self.executor.join_workers()
563
556
 
564
557
  # Return False if any of the job returns failure
@@ -612,9 +605,7 @@ class Job:
612
605
 
613
606
  if not in_f.is_file():
614
607
  continue
615
- elif (
616
- CodecInfo.get_file_ext(i) in (".txt", ".m4a") or Path(i).stem == "cover"
617
- ):
608
+ if CodecInfo.get_file_ext(i) in (".txt", ".m4a") or Path(i).stem == "cover":
618
609
  shutil.copy(in_f, output_dir / i.name)
619
610
  else:
620
611
  in_fs.append(i)
@@ -626,8 +617,6 @@ class Job:
626
617
  "bar", kwargs={"set_progress_mode": "determinate", "steps": in_fs_count}
627
618
  )
628
619
 
629
- self.executor.start_workers(processes=min(self.opt_comp.processes, in_fs_count))
630
-
631
620
  for i in in_fs:
632
621
  in_f = input_dir / i.name
633
622
  out_f = output_dir / Path(i).stem
@@ -636,6 +625,7 @@ class Job:
636
625
  work_func=StickerConvert.convert, work_args=(in_f, out_f, self.opt_comp)
637
626
  )
638
627
 
628
+ self.executor.start_workers(processes=min(self.opt_comp.processes, in_fs_count))
639
629
  self.executor.join_workers()
640
630
 
641
631
  # Return False if any of the job returns failure
@@ -669,21 +659,22 @@ class Job:
669
659
  if self.opt_output.option == "imessage":
670
660
  exporters.append(XcodeImessage.start)
671
661
 
672
- self.executor.start_workers(processes=1)
673
-
674
662
  for exporter in exporters:
675
663
  self.executor.add_work(
676
664
  work_func=exporter,
677
665
  work_args=(self.opt_output, self.opt_comp, self.opt_cred),
678
666
  )
679
667
 
668
+ self.executor.start_workers(processes=1)
680
669
  self.executor.join_workers()
681
670
 
682
671
  for result in self.executor.get_result():
683
672
  self.out_urls.extend(result)
684
673
 
685
674
  if self.out_urls:
686
- with open(Path(self.opt_output.dir, "export-result.txt"), "w+") as f:
675
+ with open(
676
+ Path(self.opt_output.dir, "export-result.txt"), "w+", encoding="utf-8"
677
+ ) as f:
687
678
  f.write("\n".join(self.out_urls))
688
679
  else:
689
680
  self.executor.cb("An error occured while exporting stickers")
@@ -697,14 +688,14 @@ class Job:
697
688
  msg += "##########\n"
698
689
  msg += "\n"
699
690
 
700
- if self.compress_fails != []:
691
+ if self.compress_fails:
701
692
  msg += f'Warning: Could not compress the following {len(self.compress_fails)} file{"s" if len(self.compress_fails) > 1 else ""}:\n'
702
693
  msg += "\n".join(self.compress_fails)
703
694
  msg += "\n"
704
695
  msg += "\nConsider adjusting compression parameters"
705
696
  msg += "\n"
706
697
 
707
- if self.out_urls != []:
698
+ if self.out_urls:
708
699
  msg += "Export results:\n"
709
700
  msg += "\n".join(self.out_urls)
710
701
  else:
@@ -24,7 +24,7 @@ class BaseOption:
24
24
  return json.dumps(self.to_dict(), indent=2)
25
25
 
26
26
  def to_dict(self) -> Dict[str, str]:
27
- return dict()
27
+ return {}
28
28
 
29
29
 
30
30
  @dataclass
@@ -45,7 +45,7 @@
45
45
  "vid_format": "Set file format if input is animated.",
46
46
  "img_format": "Set file format if input is static.",
47
47
  "fake_vid": "Convert (faking) image to video.\nUseful if:\n(1) Size limit for video is larger than image;\n(2) Mix image and video into same pack.",
48
- "scale_filter": "Set scale filter. Default as lanczos. Valid options are:\n- nearest = Use nearest neighbour (Suitable for pixel art)\n- bilinear = linear interpolation\n- bicubic = Cubic spline interpolation\n- lanczos = A high-quality downsampling filter",
48
+ "scale_filter": "Set scale filter. Default as lanczos. Valid options are:\n- nearest = Use nearest neighbour (Suitable for pixel art)\n-box = Similar to nearest, but better downscaling\n- bilinear = Linear interpolation\n- hamming = Similar to bilinear, but better downscaling\n- bicubic = Cubic spline interpolation\n- lanczos = A high-quality downsampling filter",
49
49
  "quantize_method": "Set method for quantizing image. Default as imagequant. Valid options are:\n- imagequant = Best quality but slow\n- fastoctree = Fast but image looks chunky\n- none = No image quantizing, large image size as result",
50
50
  "cache_dir": "Set custom cache directory.\nUseful for debugging, or speed up conversion if cache_dir is on RAM disk.",
51
51
  "default_emoji": "Set the default emoji for uploading Signal and Telegram sticker packs."
@@ -6,10 +6,10 @@ from pathlib import Path
6
6
  from queue import Queue
7
7
  from typing import Any, List, Union
8
8
 
9
- from sticker_convert.converter import CbQueueItemType, StickerConvert
9
+ from sticker_convert.converter import StickerConvert
10
10
  from sticker_convert.job_option import CompOption, CredOption, OutputOption
11
11
  from sticker_convert.uploaders.upload_base import UploadBase
12
- from sticker_convert.utils.callback import Callback, CallbackReturn
12
+ from sticker_convert.utils.callback import Callback, CallbackReturn, CbQueueItemType
13
13
  from sticker_convert.utils.files.cache_store import CacheStore
14
14
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
15
15
  from sticker_convert.utils.files.sanitize_filename import sanitize_filename
@@ -19,7 +19,7 @@ from sticker_convert.utils.media.format_verify import FormatVerify
19
19
 
20
20
  class CompressWastickers(UploadBase):
21
21
  def __init__(self, *args: Any, **kwargs: Any) -> None:
22
- super(CompressWastickers, self).__init__(*args, **kwargs)
22
+ super().__init__(*args, **kwargs)
23
23
  self.base_spec.size_max_img = 100000
24
24
  self.base_spec.size_max_vid = 500000
25
25
  self.base_spec.duration_min = 8
@@ -2,9 +2,8 @@
2
2
  from queue import Queue
3
3
  from typing import Union
4
4
 
5
- from sticker_convert.converter import CbQueueItemType
6
5
  from sticker_convert.job_option import CompOption, CredOption, OutputOption
7
- from sticker_convert.utils.callback import Callback, CallbackReturn
6
+ from sticker_convert.utils.callback import Callback, CallbackReturn, CbQueueItemType
8
7
 
9
8
 
10
9
  class UploadBase:
@@ -15,7 +14,7 @@ class UploadBase:
15
14
  opt_cred: CredOption,
16
15
  cb: "Union[Queue[CbQueueItemType], Callback]",
17
16
  cb_return: CallbackReturn,
18
- ):
17
+ ) -> None:
19
18
  if not cb:
20
19
  cb = Callback(silent=True)
21
20
  cb_return = CallbackReturn()
@@ -9,10 +9,10 @@ from signalstickers_client import StickersClient # type: ignore
9
9
  from signalstickers_client.errors import SignalException # type: ignore
10
10
  from signalstickers_client.models import LocalStickerPack, Sticker # type: ignore
11
11
 
12
- from sticker_convert.converter import CbQueueItemType, StickerConvert
12
+ from sticker_convert.converter import StickerConvert
13
13
  from sticker_convert.job_option import CompOption, CredOption, OutputOption
14
14
  from sticker_convert.uploaders.upload_base import UploadBase
15
- from sticker_convert.utils.callback import Callback, CallbackReturn
15
+ from sticker_convert.utils.callback import Callback, CallbackReturn, CbQueueItemType
16
16
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
17
17
  from sticker_convert.utils.media.codec_info import CodecInfo
18
18
  from sticker_convert.utils.media.format_verify import FormatVerify
@@ -20,7 +20,7 @@ from sticker_convert.utils.media.format_verify import FormatVerify
20
20
 
21
21
  class UploadSignal(UploadBase):
22
22
  def __init__(self, *args: Any, **kwargs: Any) -> None:
23
- super(UploadSignal, self).__init__(*args, **kwargs)
23
+ super().__init__(*args, **kwargs)
24
24
 
25
25
  self.base_spec.set_size_max(300000)
26
26
  self.base_spec.set_res_max(512)
@@ -49,7 +49,7 @@ class UploadSignal(UploadBase):
49
49
 
50
50
  def add_stickers_to_pack(
51
51
  self, pack: LocalStickerPack, stickers: List[Path], emoji_dict: Dict[str, str]
52
- ):
52
+ ) -> None:
53
53
  for src in stickers:
54
54
  self.cb.put(f"Verifying {src} for uploading to signal")
55
55
 
@@ -117,7 +117,7 @@ class UploadSignal(UploadBase):
117
117
  msg_block += f"Default emoji is set to {self.opt_comp.default_emoji}.\n"
118
118
  msg_block += "Please edit emoji.txt now, then continue"
119
119
  MetadataHandler.generate_emoji_file(
120
- dir=self.opt_output.dir, default_emoji=self.opt_comp.default_emoji
120
+ directory=self.opt_output.dir, default_emoji=self.opt_comp.default_emoji
121
121
  )
122
122
 
123
123
  self.cb.put(("msg_block", (msg_block,), None))
@@ -9,17 +9,17 @@ import anyio
9
9
  from telegram import Bot, InputSticker, Sticker
10
10
  from telegram.error import TelegramError
11
11
 
12
- from sticker_convert.converter import CbQueueItemType, StickerConvert
12
+ from sticker_convert.converter import StickerConvert
13
13
  from sticker_convert.job_option import CompOption, CredOption, OutputOption
14
14
  from sticker_convert.uploaders.upload_base import UploadBase
15
- from sticker_convert.utils.callback import Callback, CallbackReturn
15
+ from sticker_convert.utils.callback import Callback, CallbackReturn, CbQueueItemType
16
16
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
17
17
  from sticker_convert.utils.media.format_verify import FormatVerify
18
18
 
19
19
 
20
20
  class UploadTelegram(UploadBase):
21
21
  def __init__(self, *args: Any, **kwargs: Any) -> None:
22
- super(UploadTelegram, self).__init__(*args, **kwargs)
22
+ super().__init__(*args, **kwargs)
23
23
 
24
24
  self.base_spec.size_max_img = 512000
25
25
  self.base_spec.size_max_vid = 256000
@@ -209,6 +209,7 @@ class UploadTelegram(UploadBase):
209
209
  Path(f"bytes{ext}"), # type: ignore
210
210
  self.opt_comp_cover_merged,
211
211
  self.cb,
212
+ self.cb_return,
212
213
  )
213
214
 
214
215
  try:
@@ -248,7 +249,7 @@ class UploadTelegram(UploadBase):
248
249
  msg_block += f"Default emoji is set to {self.opt_comp.default_emoji}.\n"
249
250
  msg_block += "Please edit emoji.txt now, then continue"
250
251
  MetadataHandler.generate_emoji_file(
251
- dir=self.opt_output.dir, default_emoji=self.opt_comp.default_emoji
252
+ directory=self.opt_output.dir, default_emoji=self.opt_comp.default_emoji
252
253
  )
253
254
 
254
255
  self.cb.put(("msg_block", (msg_block,), None))
@@ -7,77 +7,22 @@ import shutil
7
7
  import zipfile
8
8
  from pathlib import Path
9
9
  from queue import Queue
10
- from typing import Any, Dict, List, Tuple, Union
10
+ from typing import Any, Dict, List, Union
11
11
 
12
- from sticker_convert.converter import CbQueueItemType, StickerConvert
12
+ from sticker_convert.converter import StickerConvert
13
13
  from sticker_convert.definitions import ROOT_DIR
14
14
  from sticker_convert.job_option import CompOption, CredOption, OutputOption
15
15
  from sticker_convert.uploaders.upload_base import UploadBase
16
- from sticker_convert.utils.callback import Callback, CallbackReturn
17
- from sticker_convert.utils.files.metadata_handler import MetadataHandler
16
+ from sticker_convert.utils.callback import Callback, CallbackReturn, CbQueueItemType
17
+ from sticker_convert.utils.files.metadata_handler import XCODE_IMESSAGE_ICONSET, MetadataHandler
18
18
  from sticker_convert.utils.files.sanitize_filename import sanitize_filename
19
19
  from sticker_convert.utils.media.codec_info import CodecInfo
20
20
  from sticker_convert.utils.media.format_verify import FormatVerify
21
21
 
22
22
 
23
- class XcodeImessageIconset:
24
- iconset: Dict[str, Tuple[int, int]] = {}
25
-
26
- def __init__(self) -> None:
27
- if self.iconset != {}:
28
- return
29
-
30
- if (ROOT_DIR / "ios-message-stickers-template").is_dir():
31
- with open(
32
- ROOT_DIR
33
- / "ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Contents.json"
34
- ) as f:
35
- dict = json.load(f)
36
- elif (ROOT_DIR / "ios-message-stickers-template.zip").is_file():
37
- with zipfile.ZipFile(
38
- (ROOT_DIR / "ios-message-stickers-template.zip"), "r"
39
- ) as f:
40
- dict = json.loads(
41
- f.read(
42
- "stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Contents.json"
43
- ).decode()
44
- )
45
- else:
46
- raise FileNotFoundError("ios-message-stickers-template not found")
47
-
48
- for i in dict["images"]:
49
- filename = i["filename"]
50
- size = i["size"]
51
- size_w = int(size.split("x")[0])
52
- size_h = int(size.split("x")[1])
53
- scale = int(i["scale"].replace("x", ""))
54
- size_w_scaled = size_w * scale
55
- size_h_scaled = size_h * scale
56
-
57
- self.iconset[filename] = (size_w_scaled, size_h_scaled)
58
-
59
- # self.iconset = {
60
- # 'App-Store-1024x1024pt.png': (1024, 1024),
61
- # 'iPad-Settings-29pt@2x.png': (58, 58),
62
- # 'iPhone-settings-29pt@2x.png': (58, 58),
63
- # 'iPhone-settings-29pt@3x.png': (87, 87),
64
- # 'Messages27x20pt@2x.png': (54, 40),
65
- # 'Messages27x20pt@3x.png': (81, 60),
66
- # 'Messages32x24pt@2x.png': (64, 48),
67
- # 'Messages32x24pt@3x.png': (96, 72),
68
- # 'Messages-App-Store-1024x768pt.png': (1024, 768),
69
- # 'Messages-iPad-67x50pt@2x.png': (134, 100),
70
- # 'Messages-iPad-Pro-74x55pt@2x.png': (148, 110),
71
- # 'Messages-iPhone-60x45pt@2x.png': (120, 90),
72
- # 'Messages-iPhone-60x45pt@3x.png': (180, 135)
73
- # }
74
-
75
-
76
23
  class XcodeImessage(UploadBase):
77
24
  def __init__(self, *args: Any, **kwargs: Any) -> None:
78
- super(XcodeImessage, self).__init__(*args, **kwargs)
79
- self.iconset = XcodeImessageIconset().iconset
80
-
25
+ super().__init__(*args, **kwargs)
81
26
  self.base_spec.set_size_max(500000)
82
27
  self.base_spec.set_res(300)
83
28
  self.base_spec.set_format(("png", ".apng", ".gif", ".jpeg", "jpg"))
@@ -169,16 +114,16 @@ class XcodeImessage(UploadBase):
169
114
  else:
170
115
  icon_source = first_image_path
171
116
 
172
- for icon, res in self.iconset.items():
117
+ for icon, res in XCODE_IMESSAGE_ICONSET.items():
173
118
  spec_cover = CompOption()
174
119
  spec_cover.set_res_w(res[0])
175
120
  spec_cover.set_res_h(res[1])
176
121
  spec_cover.set_fps(0)
177
122
 
178
123
  icon_path = self.opt_output.dir / icon
179
- if Path(icon) in [
180
- i for i in self.opt_output.dir.iterdir()
181
- ] and not FormatVerify.check_file(icon_path, spec=spec_cover):
124
+ if Path(icon) in list(
125
+ self.opt_output.dir.iterdir()
126
+ ) and not FormatVerify.check_file(icon_path, spec=spec_cover):
182
127
  StickerConvert.convert(
183
128
  icon_path, icon_path, spec_cover, self.cb, self.cb_return
184
129
  )
@@ -264,7 +209,7 @@ class XcodeImessage(UploadBase):
264
209
  if (
265
210
  CodecInfo.get_file_ext(i) == ".png"
266
211
  and i.stem != "cover"
267
- and i.name not in self.iconset
212
+ and i.name not in XCODE_IMESSAGE_ICONSET
268
213
  ):
269
214
  sticker_dir = f"{i.stem}.sticker" # 0.sticker
270
215
  stickers_lst.append(sticker_dir)
@@ -283,18 +228,18 @@ class XcodeImessage(UploadBase):
283
228
  }
284
229
 
285
230
  # packname StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/0.sticker/Contents.json
286
- with open(sticker_path / "Contents.json", "w+") as f:
231
+ with open(sticker_path / "Contents.json", "w+", encoding="utf-8") as f:
287
232
  json.dump(sticker_json_content, f, indent=2)
288
233
 
289
234
  # packname StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Contents.json
290
- with open(stickers_path / "Contents.json") as f:
235
+ with open(stickers_path / "Contents.json", encoding="utf-8") as f:
291
236
  stickerpack_json_content: Dict[str, List[Dict[str, str]]] = json.load(f)
292
237
 
293
238
  stickerpack_json_content["stickers"] = []
294
239
  for sticker in stickers_lst:
295
240
  stickerpack_json_content["stickers"].append({"filename": sticker})
296
241
 
297
- with open(stickers_path / "Contents.json", "w+") as f:
242
+ with open(stickers_path / "Contents.json", "w+", encoding="utf-8") as f:
298
243
  json.dump(stickerpack_json_content, f, indent=2)
299
244
 
300
245
  # packname StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset
@@ -308,7 +253,7 @@ class XcodeImessage(UploadBase):
308
253
  os.remove(iconset_path / iconfile_name)
309
254
 
310
255
  icons_lst: List[str] = []
311
- for icon in self.iconset:
256
+ for icon in XCODE_IMESSAGE_ICONSET:
312
257
  shutil.copy(self.opt_output.dir / icon, iconset_path / icon)
313
258
  icons_lst.append(icon)
314
259