sticker-convert 2.8.0__py3-none-any.whl → 2.8.2__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. sticker_convert/cli.py +4 -0
  2. sticker_convert/converter.py +8 -8
  3. sticker_convert/downloaders/download_base.py +3 -7
  4. sticker_convert/downloaders/download_kakao.py +3 -4
  5. sticker_convert/downloaders/download_line.py +3 -4
  6. sticker_convert/downloaders/download_signal.py +3 -4
  7. sticker_convert/downloaders/download_telegram.py +3 -4
  8. sticker_convert/gui.py +3 -1
  9. sticker_convert/gui_components/windows/advanced_compression_window.py +117 -101
  10. sticker_convert/job.py +81 -89
  11. sticker_convert/job_option.py +3 -0
  12. sticker_convert/resources/compression.json +11 -0
  13. sticker_convert/resources/help.json +1 -0
  14. sticker_convert/uploaders/compress_wastickers.py +3 -4
  15. sticker_convert/uploaders/upload_base.py +2 -8
  16. sticker_convert/uploaders/upload_signal.py +3 -4
  17. sticker_convert/uploaders/upload_telegram.py +3 -4
  18. sticker_convert/uploaders/xcode_imessage.py +3 -4
  19. sticker_convert/utils/callback.py +33 -21
  20. sticker_convert/utils/media/codec_info.py +3 -7
  21. sticker_convert/version.py +1 -1
  22. {sticker_convert-2.8.0.dist-info → sticker_convert-2.8.2.dist-info}/METADATA +18 -15
  23. {sticker_convert-2.8.0.dist-info → sticker_convert-2.8.2.dist-info}/RECORD +27 -27
  24. {sticker_convert-2.8.0.dist-info → sticker_convert-2.8.2.dist-info}/LICENSE +0 -0
  25. {sticker_convert-2.8.0.dist-info → sticker_convert-2.8.2.dist-info}/WHEEL +0 -0
  26. {sticker_convert-2.8.0.dist-info → sticker_convert-2.8.2.dist-info}/entry_points.txt +0 -0
  27. {sticker_convert-2.8.0.dist-info → sticker_convert-2.8.2.dist-info}/top_level.txt +0 -0
sticker_convert/job.py CHANGED
@@ -5,12 +5,10 @@ import os
5
5
  import shutil
6
6
  import traceback
7
7
  from datetime import datetime
8
- from multiprocessing import Process, Value
9
- from multiprocessing.managers import ListProxy, SyncManager
8
+ from multiprocessing import Manager, Process, Value
10
9
  from pathlib import Path
11
- from queue import Queue
12
10
  from threading import Thread
13
- from typing import TYPE_CHECKING, Any, Callable, Dict, Generator, List, Optional, Tuple
11
+ from typing import Any, Callable, Dict, List, Optional, Tuple
14
12
  from urllib.parse import urlparse
15
13
 
16
14
  from sticker_convert.converter import StickerConvert
@@ -23,18 +21,11 @@ from sticker_convert.uploaders.compress_wastickers import CompressWastickers
23
21
  from sticker_convert.uploaders.upload_signal import UploadSignal
24
22
  from sticker_convert.uploaders.upload_telegram import UploadTelegram
25
23
  from sticker_convert.uploaders.xcode_imessage import XcodeImessage
26
- from sticker_convert.utils.callback import CallbackReturn, CbQueueItemType
24
+ from sticker_convert.utils.callback import CallbackReturn, CbQueueType, ResultsListType, WorkQueueType
27
25
  from sticker_convert.utils.files.json_resources_loader import OUTPUT_JSON
28
26
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
29
27
  from sticker_convert.utils.media.codec_info import CodecInfo
30
28
 
31
- WorkListItemType = Optional[Tuple[Callable[..., Any], Tuple[Any, ...]]]
32
- if TYPE_CHECKING:
33
- # mypy complains about this
34
- WorkListType = ListProxy[WorkListItemType] # type: ignore
35
- else:
36
- WorkListType = List[WorkListItemType]
37
-
38
29
 
39
30
  class Executor:
40
31
  def __init__(
@@ -51,33 +42,29 @@ class Executor:
51
42
  self.cb_ask_bool = cb_ask_bool
52
43
  self.cb_ask_str = cb_ask_str
53
44
 
54
- self.manager = SyncManager()
55
- self.manager.start()
56
- # Using list instead of queue for work_list as it can cause random deadlocks
57
- # Especially when using scale_filter=nearest
58
- self.work_list: WorkListType = self.manager.list()
59
- self.results_queue: Queue[Any] = self.manager.Queue()
60
- self.cb_queue: Queue[CbQueueItemType] = self.manager.Queue()
61
- self.cb_return = CallbackReturn()
45
+ self.manager = Manager()
46
+ self.work_queue: WorkQueueType = self.manager.Queue()
47
+ self.cb_queue: CbQueueType = self.manager.Queue()
48
+ self.results_list: ResultsListType = self.manager.list()
49
+ self.cb_return = CallbackReturn(self.manager)
62
50
  self.processes: List[Process] = []
63
51
 
64
52
  self.is_cancel_job = Value("i", 0)
65
53
 
66
- self.cb_thread_instance = Thread(
67
- target=self.cb_thread,
68
- args=(
69
- self.cb_queue,
70
- self.cb_return,
71
- ),
72
- )
73
- self.cb_thread_instance.start()
54
+ self.cb_thread_instance: Optional[Thread] = None
74
55
 
75
56
  def cb_thread(
76
57
  self,
77
- cb_queue: Queue[CbQueueItemType],
78
- cb_return: CallbackReturn,
58
+ cb_queue: CbQueueType,
59
+ processes: int,
79
60
  ) -> None:
61
+ processes_done = 0
80
62
  for i in iter(cb_queue.get, None):
63
+ if i == "__PROCESS_DONE__":
64
+ processes_done += 1
65
+ if processes_done == processes:
66
+ cb_queue.put(None)
67
+ continue
81
68
  if isinstance(i, tuple):
82
69
  action = i[0]
83
70
  if len(i) >= 2:
@@ -92,42 +79,44 @@ class Executor:
92
79
  action = i
93
80
  args = tuple()
94
81
  kwargs = {}
95
- if action == "msg":
96
- self.cb_msg(*args, **kwargs)
97
- elif action == "bar":
98
- self.cb_bar(*args, **kwargs)
99
- elif action == "update_bar":
100
- self.cb_bar(update_bar=1)
101
- elif action == "msg_block":
102
- cb_return.set_response(self.cb_msg_block(*args, **kwargs))
103
- elif action == "ask_bool":
104
- cb_return.set_response(self.cb_ask_bool(*args, **kwargs))
105
- elif action == "ask_str":
106
- cb_return.set_response(self.cb_ask_str(*args, **kwargs))
107
- else:
108
- self.cb_msg(action)
82
+ self.cb(action, args, kwargs)
83
+
84
+ def cb(
85
+ self,
86
+ action: Optional[str],
87
+ args: Optional[Tuple[str, ...]] = None,
88
+ kwargs: Optional[Dict[str, Any]] = None,
89
+ ) -> None:
90
+ if args is None:
91
+ args = tuple()
92
+ if kwargs is None:
93
+ kwargs = {}
94
+ if action == "msg":
95
+ self.cb_msg(*args, **kwargs)
96
+ elif action == "bar":
97
+ self.cb_bar(*args, **kwargs)
98
+ elif action == "update_bar":
99
+ self.cb_bar(update_bar=1)
100
+ elif action == "msg_block":
101
+ self.cb_return.set_response(self.cb_msg_block(*args, **kwargs))
102
+ elif action == "ask_bool":
103
+ self.cb_return.set_response(self.cb_ask_bool(*args, **kwargs))
104
+ elif action == "ask_str":
105
+ self.cb_return.set_response(self.cb_ask_str(*args, **kwargs))
106
+ else:
107
+ self.cb_msg(action)
109
108
 
110
109
  @staticmethod
111
110
  def worker(
112
- work_list: WorkListType,
113
- results_queue: Queue[Any],
114
- cb_queue: Queue[CbQueueItemType],
111
+ work_queue: WorkQueueType,
112
+ results_list: ResultsListType,
113
+ cb_queue: CbQueueType,
115
114
  cb_return: CallbackReturn,
116
115
  ) -> None:
117
- while True:
118
- try:
119
- work = work_list.pop(0)
120
- except IndexError:
121
- break
122
-
123
- if work is None:
124
- break
125
- else:
126
- work_func, work_args = work
127
-
116
+ for work_func, work_args in iter(work_queue.get, None):
128
117
  try:
129
118
  results = work_func(*work_args, cb_queue, cb_return)
130
- results_queue.put(results)
119
+ results_list.append(results)
131
120
  except Exception:
132
121
  arg_dump: List[Any] = []
133
122
  for i in work_args:
@@ -142,15 +131,25 @@ class Executor:
142
131
  e += "#####################"
143
132
  cb_queue.put(e)
144
133
 
145
- work_list.append(None)
134
+ work_queue.put(None)
135
+ cb_queue.put("__PROCESS_DONE__")
146
136
 
147
137
  def start_workers(self, processes: int = 1) -> None:
138
+ self.cb_thread_instance = Thread(
139
+ target=self.cb_thread,
140
+ args=(self.cb_queue, processes),
141
+ )
142
+ self.cb_thread_instance.start()
143
+
144
+ self.results_list[:] = []
145
+ while not self.work_queue.empty():
146
+ self.work_queue.get()
148
147
  for _ in range(processes):
149
148
  process = Process(
150
149
  target=Executor.worker,
151
150
  args=(
152
- self.work_list,
153
- self.results_queue,
151
+ self.work_queue,
152
+ self.results_list,
154
153
  self.cb_queue,
155
154
  self.cb_return,
156
155
  ),
@@ -163,18 +162,16 @@ class Executor:
163
162
  def add_work(
164
163
  self, work_func: Callable[..., Any], work_args: Tuple[Any, ...]
165
164
  ) -> None:
166
- self.work_list.append((work_func, work_args))
165
+ self.work_queue.put((work_func, work_args))
167
166
 
168
167
  def join_workers(self) -> None:
169
- self.work_list.append(None)
168
+ self.work_queue.put(None)
170
169
  try:
171
170
  for process in self.processes:
172
171
  process.join()
173
172
  except KeyboardInterrupt:
174
173
  pass
175
174
 
176
- self.results_queue.put(None)
177
- self.work_list[:] = []
178
175
  self.processes.clear()
179
176
 
180
177
  def kill_workers(self, *_: Any, **__: Any) -> None:
@@ -183,25 +180,14 @@ class Executor:
183
180
  for process in self.processes:
184
181
  process.terminate()
185
182
 
186
- self.cleanup(killed=True)
183
+ self.cb_msg("Job cancelled.")
184
+ self.cleanup()
187
185
 
188
- def cleanup(self, killed: bool = False) -> None:
189
- if killed:
190
- self.cb_queue.put("Job cancelled.")
191
- self.cb_queue.put(("bar", None, {"set_progress_mode": "clear"}))
186
+ def cleanup(self) -> None:
187
+ self.cb_bar("clear")
192
188
  self.cb_queue.put(None)
193
- self.cb_thread_instance.join()
194
-
195
- def get_result(self) -> Generator[Any, None, None]:
196
- yield from iter(self.results_queue.get, None)
197
-
198
- def cb(
199
- self,
200
- action: Optional[str],
201
- args: Optional[Tuple[str, ...]] = None,
202
- kwargs: Optional[Dict[str, Any]] = None,
203
- ) -> None:
204
- self.cb_queue.put((action, args, kwargs))
189
+ if self.cb_thread_instance:
190
+ self.cb_thread_instance.join()
205
191
 
206
192
 
207
193
  class Job:
@@ -547,17 +533,18 @@ class Job:
547
533
  self.executor.cb("Nothing to download")
548
534
  return True
549
535
 
536
+ self.executor.start_workers(processes=1)
537
+
550
538
  for downloader in downloaders:
551
539
  self.executor.add_work(
552
540
  work_func=downloader,
553
541
  work_args=(self.opt_input.url, self.opt_input.dir, self.opt_cred),
554
542
  )
555
543
 
556
- self.executor.start_workers(processes=1)
557
544
  self.executor.join_workers()
558
545
 
559
546
  # Return False if any of the job returns failure
560
- for result in self.executor.get_result():
547
+ for result in self.executor.results_list:
561
548
  if result is False:
562
549
  return False
563
550
 
@@ -613,12 +600,17 @@ class Job:
613
600
  in_fs.append(i)
614
601
 
615
602
  in_fs_count = len(in_fs)
603
+ if in_fs_count == 0:
604
+ self.executor.cb("No files to compress")
605
+ return True
616
606
 
617
607
  self.executor.cb(msg)
618
608
  self.executor.cb(
619
609
  "bar", kwargs={"set_progress_mode": "determinate", "steps": in_fs_count}
620
610
  )
621
611
 
612
+ self.executor.start_workers(processes=min(self.opt_comp.processes, in_fs_count))
613
+
622
614
  for i in in_fs:
623
615
  in_f = input_dir / i.name
624
616
  out_f = output_dir / Path(i).stem
@@ -627,11 +619,10 @@ class Job:
627
619
  work_func=StickerConvert.convert, work_args=(in_f, out_f, self.opt_comp)
628
620
  )
629
621
 
630
- self.executor.start_workers(processes=min(self.opt_comp.processes, in_fs_count))
631
622
  self.executor.join_workers()
632
623
 
633
624
  # Return False if any of the job returns failure
634
- for result in self.executor.get_result():
625
+ for result in self.executor.results_list:
635
626
  if result[0] is False:
636
627
  return False
637
628
 
@@ -661,16 +652,17 @@ class Job:
661
652
  if self.opt_output.option == "imessage":
662
653
  exporters.append(XcodeImessage.start)
663
654
 
655
+ self.executor.start_workers(processes=1)
656
+
664
657
  for exporter in exporters:
665
658
  self.executor.add_work(
666
659
  work_func=exporter,
667
660
  work_args=(self.opt_output, self.opt_comp, self.opt_cred),
668
661
  )
669
662
 
670
- self.executor.start_workers(processes=1)
671
663
  self.executor.join_workers()
672
664
 
673
- for result in self.executor.get_result():
665
+ for result in self.executor.results_list:
674
666
  self.out_urls.extend(result)
675
667
 
676
668
  if self.out_urls:
@@ -69,6 +69,8 @@ class CompOption(BaseOption):
69
69
 
70
70
  bg_color: Optional[str] = None
71
71
 
72
+ padding_percent: int = 0
73
+
72
74
  steps: int = 1
73
75
  fake_vid: Optional[bool] = None
74
76
  quantize_method: Optional[str] = None
@@ -104,6 +106,7 @@ class CompOption(BaseOption):
104
106
  "duration": {"min": self.duration_min, "max": self.duration_max},
105
107
  "steps": self.steps,
106
108
  "bg_color": self.bg_color,
109
+ "padding_percent": self.padding_percent,
107
110
  "fake_vid": self.fake_vid,
108
111
  "quantize_method": self.quantize_method,
109
112
  "scale_filter": self.scale_filter,
@@ -38,6 +38,7 @@
38
38
  "min": 0,
39
39
  "max": 10000
40
40
  },
41
+ "padding_percent": 0,
41
42
  "bg_color": "",
42
43
  "steps": 16,
43
44
  "fake_vid": false,
@@ -84,6 +85,7 @@
84
85
  "min": 0,
85
86
  "max": 3000
86
87
  },
88
+ "padding_percent": 0,
87
89
  "bg_color": "",
88
90
  "steps": 16,
89
91
  "fake_vid": false,
@@ -130,6 +132,7 @@
130
132
  "min": 0,
131
133
  "max": 3000
132
134
  },
135
+ "padding_percent": 0,
133
136
  "bg_color": "",
134
137
  "steps": 16,
135
138
  "fake_vid": false,
@@ -176,6 +179,7 @@
176
179
  "min": 0,
177
180
  "max": 3000
178
181
  },
182
+ "padding_percent": 0,
179
183
  "bg_color": "",
180
184
  "steps": 16,
181
185
  "fake_vid": false,
@@ -222,6 +226,7 @@
222
226
  "min": 8,
223
227
  "max": 10000
224
228
  },
229
+ "padding_percent": 0,
225
230
  "bg_color": "",
226
231
  "steps": 16,
227
232
  "fake_vid": true,
@@ -268,6 +273,7 @@
268
273
  "min": 83,
269
274
  "max": 4000
270
275
  },
276
+ "padding_percent": 0,
271
277
  "bg_color": "",
272
278
  "steps": 16,
273
279
  "fake_vid": false,
@@ -314,6 +320,7 @@
314
320
  "min": 0,
315
321
  "max": 5000
316
322
  },
323
+ "padding_percent": 0,
317
324
  "bg_color": "",
318
325
  "steps": 16,
319
326
  "fake_vid": false,
@@ -360,6 +367,7 @@
360
367
  "min": 0,
361
368
  "max": 2000
362
369
  },
370
+ "padding_percent": 0,
363
371
  "bg_color": "",
364
372
  "steps": 16,
365
373
  "fake_vid": false,
@@ -406,6 +414,7 @@
406
414
  "min": 0,
407
415
  "max": 2000
408
416
  },
417
+ "padding_percent": 0,
409
418
  "bg_color": "",
410
419
  "steps": 16,
411
420
  "fake_vid": false,
@@ -452,6 +461,7 @@
452
461
  "min": 0,
453
462
  "max": 2000
454
463
  },
464
+ "padding_percent": 0,
455
465
  "bg_color": "",
456
466
  "steps": 16,
457
467
  "fake_vid": false,
@@ -498,6 +508,7 @@
498
508
  "min": 0,
499
509
  "max": 10000
500
510
  },
511
+ "padding_percent": 0,
501
512
  "bg_color": "",
502
513
  "steps": 16,
503
514
  "fake_vid": false,
@@ -39,6 +39,7 @@
39
39
  "duration": "Change playback speed if outside of duration limit.\nDuration set in miliseconds.\n0 will disable limit.",
40
40
  "duration_min": "Set minimum output duration in miliseconds.",
41
41
  "duration_max": "Set maximum output duration in miliseconds.",
42
+ "padding_percent": "Set percentage of space used as padding.",
42
43
  "bg_color": "Set custom background color.\nExample: 00ff00 for green.\nIf this is not set, background color would be auto set to black if image is bright, or white if image is dark.\nNote: The color should not be visible if output format supports transparency.",
43
44
  "size": "Set maximum file size in bytes for video and image.",
44
45
  "vid_size_max": "Set maximum file size limit for animated stickers.",
@@ -3,13 +3,12 @@ import copy
3
3
  import shutil
4
4
  import zipfile
5
5
  from pathlib import Path
6
- from queue import Queue
7
- from typing import Any, List, Union
6
+ from typing import Any, List
8
7
 
9
8
  from sticker_convert.converter import StickerConvert
10
9
  from sticker_convert.job_option import CompOption, CredOption, OutputOption
11
10
  from sticker_convert.uploaders.upload_base import UploadBase
12
- from sticker_convert.utils.callback import Callback, CallbackReturn, CbQueueItemType
11
+ from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
13
12
  from sticker_convert.utils.files.cache_store import CacheStore
14
13
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
15
14
  from sticker_convert.utils.files.sanitize_filename import sanitize_filename
@@ -150,7 +149,7 @@ class CompressWastickers(UploadBase):
150
149
  opt_output: OutputOption,
151
150
  opt_comp: CompOption,
152
151
  opt_cred: CredOption,
153
- cb: "Union[Queue[CbQueueItemType], Callback]",
152
+ cb: CallbackProtocol,
154
153
  cb_return: CallbackReturn,
155
154
  ) -> List[str]:
156
155
  exporter = CompressWastickers(opt_output, opt_comp, opt_cred, cb, cb_return)
@@ -1,9 +1,7 @@
1
1
  #!/usr/bin/env python3
2
- from queue import Queue
3
- from typing import Union
4
2
 
5
3
  from sticker_convert.job_option import CompOption, CredOption, OutputOption
6
- from sticker_convert.utils.callback import Callback, CallbackReturn, CbQueueItemType
4
+ from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
7
5
 
8
6
 
9
7
  class UploadBase:
@@ -12,13 +10,9 @@ class UploadBase:
12
10
  opt_output: OutputOption,
13
11
  opt_comp: CompOption,
14
12
  opt_cred: CredOption,
15
- cb: "Union[Queue[CbQueueItemType], Callback]",
13
+ cb: CallbackProtocol,
16
14
  cb_return: CallbackReturn,
17
15
  ) -> None:
18
- if not cb:
19
- cb = Callback(silent=True)
20
- cb_return = CallbackReturn()
21
-
22
16
  self.opt_output = opt_output
23
17
  self.opt_comp = opt_comp
24
18
  self.opt_cred = opt_cred
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
  import copy
3
3
  from pathlib import Path
4
- from queue import Queue
5
- from typing import Any, Dict, List, Union
4
+ from typing import Any, Dict, List
6
5
 
7
6
  import anyio
8
7
  from signalstickers_client import StickersClient # type: ignore
@@ -12,7 +11,7 @@ from signalstickers_client.models import LocalStickerPack, Sticker # type: igno
12
11
  from sticker_convert.converter import StickerConvert
13
12
  from sticker_convert.job_option import CompOption, CredOption, OutputOption
14
13
  from sticker_convert.uploaders.upload_base import UploadBase
15
- from sticker_convert.utils.callback import Callback, CallbackReturn, CbQueueItemType
14
+ from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
16
15
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
17
16
  from sticker_convert.utils.media.codec_info import CodecInfo
18
17
  from sticker_convert.utils.media.format_verify import FormatVerify
@@ -167,7 +166,7 @@ class UploadSignal(UploadBase):
167
166
  opt_output: OutputOption,
168
167
  opt_comp: CompOption,
169
168
  opt_cred: CredOption,
170
- cb: "Union[Queue[CbQueueItemType], Callback]",
169
+ cb: CallbackProtocol,
171
170
  cb_return: CallbackReturn,
172
171
  ) -> List[str]:
173
172
  exporter = UploadSignal(opt_output, opt_comp, opt_cred, cb, cb_return)
@@ -2,7 +2,6 @@
2
2
  import copy
3
3
  import re
4
4
  from pathlib import Path
5
- from queue import Queue
6
5
  from typing import Any, Dict, List, Optional, Tuple, Union, cast
7
6
 
8
7
  import anyio
@@ -13,7 +12,7 @@ from telegram.ext import AIORateLimiter, ApplicationBuilder
13
12
  from sticker_convert.converter import StickerConvert
14
13
  from sticker_convert.job_option import CompOption, CredOption, OutputOption
15
14
  from sticker_convert.uploaders.upload_base import UploadBase
16
- from sticker_convert.utils.callback import Callback, CallbackReturn, CbQueueItemType
15
+ from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
17
16
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
18
17
  from sticker_convert.utils.media.format_verify import FormatVerify
19
18
 
@@ -79,7 +78,7 @@ class UploadTelegram(UploadBase):
79
78
  application = ( # type: ignore
80
79
  ApplicationBuilder()
81
80
  .token(self.opt_cred.telegram_token.strip())
82
- .rate_limiter(AIORateLimiter())
81
+ .rate_limiter(AIORateLimiter(max_retries=3))
83
82
  .connect_timeout(timeout)
84
83
  .pool_timeout(timeout)
85
84
  .read_timeout(timeout)
@@ -350,7 +349,7 @@ class UploadTelegram(UploadBase):
350
349
  opt_output: OutputOption,
351
350
  opt_comp: CompOption,
352
351
  opt_cred: CredOption,
353
- cb: "Union[Queue[CbQueueItemType], Callback]",
352
+ cb: CallbackProtocol,
354
353
  cb_return: CallbackReturn,
355
354
  ) -> List[str]:
356
355
  exporter = UploadTelegram(
@@ -6,14 +6,13 @@ import plistlib
6
6
  import shutil
7
7
  import zipfile
8
8
  from pathlib import Path
9
- from queue import Queue
10
- from typing import Any, Dict, List, Union
9
+ from typing import Any, Dict, List
11
10
 
12
11
  from sticker_convert.converter import StickerConvert
13
12
  from sticker_convert.definitions import ROOT_DIR
14
13
  from sticker_convert.job_option import CompOption, CredOption, OutputOption
15
14
  from sticker_convert.uploaders.upload_base import UploadBase
16
- from sticker_convert.utils.callback import Callback, CallbackReturn, CbQueueItemType
15
+ from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
17
16
  from sticker_convert.utils.files.metadata_handler import XCODE_IMESSAGE_ICONSET, MetadataHandler
18
17
  from sticker_convert.utils.files.sanitize_filename import sanitize_filename
19
18
  from sticker_convert.utils.media.codec_info import CodecInfo
@@ -279,7 +278,7 @@ class XcodeImessage(UploadBase):
279
278
  opt_output: OutputOption,
280
279
  opt_comp: CompOption,
281
280
  opt_cred: CredOption,
282
- cb: "Union[Queue[CbQueueItemType], Callback]",
281
+ cb: CallbackProtocol,
283
282
  cb_return: CallbackReturn,
284
283
  ) -> List[str]:
285
284
  exporter = XcodeImessage(opt_output, opt_comp, opt_cred, cb, cb_return)
@@ -1,39 +1,57 @@
1
1
  #!/usr/bin/env python3
2
- from multiprocessing import Event, Queue
3
- from typing import Any, Callable, Dict, Optional, Tuple, Union
2
+ from multiprocessing import Event, Manager
3
+ from multiprocessing.managers import ListProxy, SyncManager
4
+ from queue import Queue
5
+ from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Protocol, Tuple, Union
4
6
 
5
7
  from tqdm import tqdm
6
8
 
7
9
  CbQueueTupleType = Tuple[
8
10
  Optional[str], Optional[Tuple[Any, ...]], Optional[Dict[str, Any]]
9
11
  ]
10
- CbQueueItemType = Union[
11
- CbQueueTupleType,
12
- str,
13
- None,
14
- ]
12
+ CbQueueItemType = Union[CbQueueTupleType, str, None]
13
+ WorkQueueItemType = Optional[Tuple[Callable[..., Any], Tuple[Any, ...]]]
14
+ ResponseItemType = Union[bool, str, None]
15
+
16
+ if TYPE_CHECKING:
17
+ # mypy complains about this
18
+ ResultsListType = ListProxy[Any] # type: ignore
19
+ ResponseListType = ListProxy[ResponseItemType] # type: ignore
20
+ CbQueueType = Queue[CbQueueItemType] # type: ignore
21
+ WorkQueueType = Queue[WorkQueueItemType] # type: ignore
22
+ else:
23
+ ResultsListType = List[Any]
24
+ ResponseListType = List[ResponseItemType]
25
+ CbQueueType = Queue
26
+ WorkQueueType = Queue
15
27
 
16
28
 
17
29
  class CallbackReturn:
18
- def __init__(self) -> None:
30
+ def __init__(self, manager: Optional[SyncManager] = None) -> None:
19
31
  self.response_event = Event()
20
- self.response_queue: Queue[Union[bool, str, None]] = Queue()
32
+ if manager is None:
33
+ manager = Manager()
34
+ self.response_queue: ResponseListType = manager.list()
21
35
 
22
- def set_response(self, response: Union[bool, str, None]) -> None:
23
- self.response_queue.put(response)
36
+ def set_response(self, response: ResponseItemType) -> None:
37
+ self.response_queue.append(response)
24
38
  self.response_event.set()
25
39
 
26
- def get_response(self) -> Union[bool, str, None]:
40
+ def get_response(self) -> ResponseItemType:
27
41
  self.response_event.wait()
28
42
 
29
- response = self.response_queue.get()
43
+ response = self.response_queue.pop()
30
44
 
31
45
  self.response_event.clear()
32
46
 
33
47
  return response
34
48
 
35
49
 
36
- class Callback:
50
+ class CallbackProtocol(Protocol):
51
+ def put(self, i: Union[CbQueueItemType, str]) -> Any: ...
52
+
53
+
54
+ class Callback(CallbackProtocol):
37
55
  def __init__(
38
56
  self,
39
57
  msg: Optional[Callable[..., None]] = None,
@@ -155,13 +173,7 @@ class Callback:
155
173
 
156
174
  return response
157
175
 
158
- def put(
159
- self,
160
- i: Union[
161
- CbQueueItemType,
162
- str,
163
- ],
164
- ) -> Union[str, bool, None]:
176
+ def put(self, i: Union[CbQueueItemType, str]) -> Union[str, bool, None]:
165
177
  if isinstance(i, tuple):
166
178
  action = i[0]
167
179
  if len(i) >= 2:
@@ -258,11 +258,7 @@ class CodecInfo:
258
258
  fps = frames_apparent / total_duration * 1000
259
259
  return fps, frames, total_duration
260
260
 
261
- return (
262
- 0.0,
263
- 1,
264
- 0,
265
- )
261
+ return 0.0, 1, 0
266
262
 
267
263
  @staticmethod
268
264
  def _get_file_fps_frames_duration_webp(
@@ -294,8 +290,8 @@ class CodecInfo:
294
290
  total_duration += frame_duration
295
291
  frames += 1
296
292
 
297
- if frames == 0:
298
- return 0.0, 0, 0
293
+ if frames <= 1:
294
+ return 0.0, 1, 0
299
295
 
300
296
  if len(durations) == 1:
301
297
  fps = frames / total_duration * 1000
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
- __version__ = "2.8.0"
3
+ __version__ = "2.8.2"