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.
- sticker_convert/cli.py +12 -16
- sticker_convert/converter.py +46 -50
- sticker_convert/definitions.py +8 -12
- sticker_convert/downloaders/download_base.py +13 -30
- sticker_convert/downloaders/download_kakao.py +19 -25
- sticker_convert/downloaders/download_line.py +7 -8
- sticker_convert/downloaders/download_signal.py +4 -5
- sticker_convert/downloaders/download_telegram.py +4 -5
- sticker_convert/gui.py +28 -29
- sticker_convert/gui_components/frames/comp_frame.py +7 -7
- sticker_convert/gui_components/frames/config_frame.py +7 -7
- sticker_convert/gui_components/frames/control_frame.py +1 -1
- sticker_convert/gui_components/frames/cred_frame.py +8 -8
- sticker_convert/gui_components/frames/input_frame.py +6 -6
- sticker_convert/gui_components/frames/output_frame.py +6 -6
- sticker_convert/gui_components/frames/progress_frame.py +6 -6
- sticker_convert/gui_components/gui_utils.py +2 -4
- sticker_convert/gui_components/windows/advanced_compression_window.py +13 -11
- sticker_convert/gui_components/windows/base_window.py +3 -3
- sticker_convert/gui_components/windows/kakao_get_auth_window.py +2 -2
- sticker_convert/gui_components/windows/line_get_auth_window.py +2 -2
- sticker_convert/gui_components/windows/signal_get_auth_window.py +1 -1
- sticker_convert/job.py +66 -75
- sticker_convert/job_option.py +1 -1
- sticker_convert/resources/help.json +1 -1
- sticker_convert/uploaders/compress_wastickers.py +3 -3
- sticker_convert/uploaders/upload_base.py +2 -3
- sticker_convert/uploaders/upload_signal.py +5 -5
- sticker_convert/uploaders/upload_telegram.py +5 -4
- sticker_convert/uploaders/xcode_imessage.py +14 -69
- sticker_convert/utils/auth/get_kakao_auth.py +4 -5
- sticker_convert/utils/auth/get_line_auth.py +1 -2
- sticker_convert/utils/auth/get_signal_auth.py +4 -4
- sticker_convert/utils/callback.py +25 -17
- sticker_convert/utils/files/cache_store.py +4 -6
- sticker_convert/utils/files/json_manager.py +3 -4
- sticker_convert/utils/files/json_resources_loader.py +12 -0
- sticker_convert/utils/files/metadata_handler.py +83 -74
- sticker_convert/utils/files/run_bin.py +8 -7
- sticker_convert/utils/files/sanitize_filename.py +30 -28
- sticker_convert/utils/media/apple_png_normalize.py +2 -2
- sticker_convert/utils/media/codec_info.py +22 -29
- sticker_convert/utils/media/decrypt_kakao.py +3 -5
- sticker_convert/utils/media/format_verify.py +6 -8
- sticker_convert/utils/url_detect.py +4 -5
- sticker_convert/version.py +1 -1
- {sticker_convert-2.7.3.dist-info → sticker_convert-2.7.4.dist-info}/METADATA +16 -14
- {sticker_convert-2.7.3.dist-info → sticker_convert-2.7.4.dist-info}/RECORD +52 -51
- {sticker_convert-2.7.3.dist-info → sticker_convert-2.7.4.dist-info}/WHEEL +1 -1
- {sticker_convert-2.7.3.dist-info → sticker_convert-2.7.4.dist-info}/LICENSE +0 -0
- {sticker_convert-2.7.3.dist-info → sticker_convert-2.7.4.dist-info}/entry_points.txt +0 -0
- {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
|
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.
|
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
|
-
|
52
|
-
|
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:
|
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
|
90
|
+
kwargs: Dict[str, str] = i[2] if i[2] else {}
|
99
91
|
else:
|
100
|
-
kwargs =
|
92
|
+
kwargs = {}
|
101
93
|
else:
|
102
94
|
action = i
|
103
95
|
args = tuple()
|
104
|
-
kwargs =
|
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
|
-
|
114
|
+
work_list: WorkListType,
|
123
115
|
results_queue: Queue[Any],
|
124
|
-
cb_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
|
-
|
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
|
-
|
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.
|
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.
|
167
|
+
self.work_list.append((work_func, work_args))
|
177
168
|
|
178
169
|
def join_workers(self) -> None:
|
179
|
-
self.
|
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, *
|
181
|
+
def kill_workers(self, *_: Any, **__: Any) -> None:
|
191
182
|
self.is_cancel_job.value = 1 # type: ignore
|
192
|
-
|
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
|
-
|
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
|
-
|
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, *
|
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 =
|
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 +=
|
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
|
-
|
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(
|
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
|
-
|
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(
|
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:
|
sticker_convert/job_option.py
CHANGED
@@ -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 =
|
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
|
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(
|
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
|
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(
|
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
|
-
|
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
|
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(
|
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
|
-
|
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,
|
10
|
+
from typing import Any, Dict, List, Union
|
11
11
|
|
12
|
-
from sticker_convert.converter import
|
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(
|
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
|
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
|
-
|
181
|
-
|
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
|
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
|
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
|
|