sticker-convert 2.8.12__py3-none-any.whl → 2.17.0.0__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/__main__.py +24 -24
- sticker_convert/auth/__init__.py +0 -0
- sticker_convert/auth/auth_base.py +19 -0
- sticker_convert/auth/auth_discord.py +149 -0
- sticker_convert/{utils/auth/get_kakao_auth.py → auth/auth_kakao_android_login.py} +331 -300
- sticker_convert/auth/auth_kakao_desktop_login.py +327 -0
- sticker_convert/auth/auth_kakao_desktop_memdump.py +281 -0
- sticker_convert/{utils/auth/get_line_auth.py → auth/auth_line.py} +98 -80
- sticker_convert/auth/auth_signal.py +139 -0
- sticker_convert/auth/auth_telethon.py +161 -0
- sticker_convert/auth/auth_viber.py +250 -0
- sticker_convert/auth/telegram_api.py +736 -0
- sticker_convert/cli.py +623 -509
- sticker_convert/converter.py +1093 -962
- sticker_convert/definitions.py +11 -0
- sticker_convert/downloaders/download_band.py +111 -0
- sticker_convert/downloaders/download_base.py +171 -130
- sticker_convert/downloaders/download_discord.py +92 -0
- sticker_convert/downloaders/download_kakao.py +417 -255
- sticker_convert/downloaders/download_line.py +484 -472
- sticker_convert/downloaders/download_ogq.py +80 -0
- sticker_convert/downloaders/download_signal.py +108 -92
- sticker_convert/downloaders/download_telegram.py +56 -130
- sticker_convert/downloaders/download_viber.py +121 -95
- sticker_convert/gui.py +788 -795
- sticker_convert/gui_components/frames/comp_frame.py +180 -165
- sticker_convert/gui_components/frames/config_frame.py +156 -113
- sticker_convert/gui_components/frames/control_frame.py +32 -30
- sticker_convert/gui_components/frames/cred_frame.py +232 -162
- sticker_convert/gui_components/frames/input_frame.py +139 -137
- sticker_convert/gui_components/frames/output_frame.py +112 -110
- sticker_convert/gui_components/frames/right_clicker.py +25 -23
- sticker_convert/gui_components/windows/advanced_compression_window.py +757 -715
- sticker_convert/gui_components/windows/base_window.py +7 -2
- sticker_convert/gui_components/windows/discord_get_auth_window.py +79 -0
- sticker_convert/gui_components/windows/kakao_get_auth_window.py +511 -186
- sticker_convert/gui_components/windows/line_get_auth_window.py +94 -102
- sticker_convert/gui_components/windows/signal_get_auth_window.py +84 -135
- sticker_convert/gui_components/windows/viber_get_auth_window.py +168 -0
- sticker_convert/ios-message-stickers-template/.github/FUNDING.yml +3 -3
- sticker_convert/ios-message-stickers-template/.gitignore +0 -0
- sticker_convert/ios-message-stickers-template/README.md +10 -10
- sticker_convert/ios-message-stickers-template/stickers/Info.plist +43 -43
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Info.plist +31 -31
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Contents.json +6 -6
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Contents.json +20 -20
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 1.sticker/Contents.json +9 -9
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 1.sticker/Sticker 1.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 2.sticker/Contents.json +9 -9
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 2.sticker/Sticker 2.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 3.sticker/Contents.json +9 -9
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 3.sticker/Sticker 3.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/App-Store-1024x1024pt.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Contents.json +91 -91
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-App-Store-1024x768pt.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPad-67x50pt@2x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPad-Pro-74x55pt@2x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPhone-60x45pt@2x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPhone-60x45pt@3x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages27x20pt@2x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages27x20pt@3x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages32x24pt@2x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages32x24pt@3x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPad-Settings-29pt@2x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPhone-Settings-29pt@3x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPhone-settings-29pt@2x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.pbxproj +364 -364
- sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -7
- sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -8
- sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/xcuserdata/niklaspeterson.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- sticker_convert/ios-message-stickers-template/stickers.xcodeproj/xcuserdata/niklaspeterson.xcuserdatad/xcschemes/xcschememanagement.plist +14 -14
- sticker_convert/job.py +279 -179
- sticker_convert/job_option.py +15 -2
- sticker_convert/locales/en_US/LC_MESSAGES/base.mo +0 -0
- sticker_convert/locales/ja_JP/LC_MESSAGES/base.mo +0 -0
- sticker_convert/locales/zh_CN/LC_MESSAGES/base.mo +0 -0
- sticker_convert/locales/zh_TW/LC_MESSAGES/base.mo +0 -0
- sticker_convert/py.typed +0 -0
- sticker_convert/resources/NotoColorEmoji.ttf +0 -0
- sticker_convert/resources/compression.json +220 -16
- sticker_convert/resources/emoji.json +527 -77
- sticker_convert/resources/help.ja_JP.json +88 -0
- sticker_convert/resources/help.json +24 -10
- sticker_convert/resources/help.zh_CN.json +88 -0
- sticker_convert/resources/help.zh_TW.json +88 -0
- sticker_convert/resources/input.ja_JP.json +74 -0
- sticker_convert/resources/input.json +121 -71
- sticker_convert/resources/input.zh_CN.json +74 -0
- sticker_convert/resources/input.zh_TW.json +74 -0
- sticker_convert/resources/memdump_linux.sh +25 -0
- sticker_convert/resources/memdump_windows.ps1 +8 -0
- sticker_convert/resources/output.ja_JP.json +38 -0
- sticker_convert/resources/output.json +24 -0
- sticker_convert/resources/output.zh_CN.json +38 -0
- sticker_convert/resources/output.zh_TW.json +38 -0
- sticker_convert/uploaders/compress_wastickers.py +186 -156
- sticker_convert/uploaders/upload_base.py +44 -35
- sticker_convert/uploaders/upload_signal.py +218 -173
- sticker_convert/uploaders/upload_telegram.py +353 -388
- sticker_convert/uploaders/upload_viber.py +178 -0
- sticker_convert/uploaders/xcode_imessage.py +295 -285
- sticker_convert/utils/callback.py +238 -6
- sticker_convert/utils/chrome_remotedebug.py +219 -0
- sticker_convert/utils/chromiums/linux.py +52 -0
- sticker_convert/utils/chromiums/osx.py +68 -0
- sticker_convert/utils/chromiums/windows.py +45 -0
- sticker_convert/utils/emoji.py +28 -0
- sticker_convert/utils/files/json_resources_loader.py +24 -19
- sticker_convert/utils/files/metadata_handler.py +8 -7
- sticker_convert/utils/files/run_bin.py +1 -1
- sticker_convert/utils/media/codec_info.py +99 -67
- sticker_convert/utils/media/format_verify.py +33 -20
- sticker_convert/utils/process.py +231 -0
- sticker_convert/utils/translate.py +108 -0
- sticker_convert/utils/url_detect.py +40 -33
- sticker_convert/version.py +1 -1
- {sticker_convert-2.8.12.dist-info → sticker_convert-2.17.0.0.dist-info}/METADATA +189 -96
- sticker_convert-2.17.0.0.dist-info/RECORD +138 -0
- {sticker_convert-2.8.12.dist-info → sticker_convert-2.17.0.0.dist-info}/WHEEL +1 -1
- sticker_convert/utils/auth/get_signal_auth.py +0 -129
- sticker_convert-2.8.12.dist-info/RECORD +0 -101
- {sticker_convert-2.8.12.dist-info → sticker_convert-2.17.0.0.dist-info}/entry_points.txt +0 -0
- {sticker_convert-2.8.12.dist-info → sticker_convert-2.17.0.0.dist-info/licenses}/LICENSE +0 -0
- {sticker_convert-2.8.12.dist-info → sticker_convert-2.17.0.0.dist-info}/top_level.txt +0 -0
sticker_convert/job.py
CHANGED
|
@@ -8,12 +8,16 @@ from datetime import datetime
|
|
|
8
8
|
from multiprocessing import Manager, Process, Value
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from threading import Thread
|
|
11
|
-
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
11
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union, cast
|
|
12
12
|
from urllib.parse import urlparse
|
|
13
13
|
|
|
14
14
|
from sticker_convert.converter import StickerConvert
|
|
15
|
+
from sticker_convert.definitions import RUNTIME_STATE
|
|
16
|
+
from sticker_convert.downloaders.download_band import DownloadBand
|
|
17
|
+
from sticker_convert.downloaders.download_discord import DownloadDiscord
|
|
15
18
|
from sticker_convert.downloaders.download_kakao import DownloadKakao
|
|
16
19
|
from sticker_convert.downloaders.download_line import DownloadLine
|
|
20
|
+
from sticker_convert.downloaders.download_ogq import DownloadOgq
|
|
17
21
|
from sticker_convert.downloaders.download_signal import DownloadSignal
|
|
18
22
|
from sticker_convert.downloaders.download_telegram import DownloadTelegram
|
|
19
23
|
from sticker_convert.downloaders.download_viber import DownloadViber
|
|
@@ -21,27 +25,27 @@ from sticker_convert.job_option import CompOption, CredOption, InputOption, Outp
|
|
|
21
25
|
from sticker_convert.uploaders.compress_wastickers import CompressWastickers
|
|
22
26
|
from sticker_convert.uploaders.upload_signal import UploadSignal
|
|
23
27
|
from sticker_convert.uploaders.upload_telegram import UploadTelegram
|
|
28
|
+
from sticker_convert.uploaders.upload_viber import UploadViber
|
|
24
29
|
from sticker_convert.uploaders.xcode_imessage import XcodeImessage
|
|
25
30
|
from sticker_convert.utils.callback import CallbackReturn, CbQueueType, ResultsListType, WorkQueueType
|
|
26
|
-
from sticker_convert.utils.
|
|
31
|
+
from sticker_convert.utils.chrome_remotedebug import CRD
|
|
32
|
+
from sticker_convert.utils.files.json_resources_loader import load_resource_json
|
|
27
33
|
from sticker_convert.utils.files.metadata_handler import MetadataHandler
|
|
28
34
|
from sticker_convert.utils.media.codec_info import CodecInfo
|
|
35
|
+
from sticker_convert.utils.translate import I
|
|
36
|
+
|
|
37
|
+
if TYPE_CHECKING:
|
|
38
|
+
from sticker_convert.utils.callback import CallbackCli, CallbackGui
|
|
29
39
|
|
|
30
40
|
|
|
31
41
|
class Executor:
|
|
32
|
-
def __init__(
|
|
33
|
-
self
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
cb_bar
|
|
37
|
-
cb_ask_bool
|
|
38
|
-
cb_ask_str
|
|
39
|
-
) -> None:
|
|
40
|
-
self.cb_msg = cb_msg
|
|
41
|
-
self.cb_msg_block = cb_msg_block
|
|
42
|
-
self.cb_bar = cb_bar
|
|
43
|
-
self.cb_ask_bool = cb_ask_bool
|
|
44
|
-
self.cb_ask_str = cb_ask_str
|
|
42
|
+
def __init__(self, cb: Union[CallbackGui, CallbackCli]) -> None:
|
|
43
|
+
self.cb_msg = cb.cb_msg
|
|
44
|
+
self.cb_msg_block = cb.cb_msg_block
|
|
45
|
+
self.cb_msg_dynamic = cb.cb_msg_dynamic
|
|
46
|
+
self.cb_bar = cb.cb_bar
|
|
47
|
+
self.cb_ask_bool = cb.cb_ask_bool
|
|
48
|
+
self.cb_ask_str = cb.cb_ask_str
|
|
45
49
|
|
|
46
50
|
self.manager = Manager()
|
|
47
51
|
self.work_queue: WorkQueueType = self.manager.Queue()
|
|
@@ -85,7 +89,7 @@ class Executor:
|
|
|
85
89
|
def cb(
|
|
86
90
|
self,
|
|
87
91
|
action: Optional[str],
|
|
88
|
-
args: Optional[Tuple[
|
|
92
|
+
args: Optional[Tuple[Any, ...]] = None,
|
|
89
93
|
kwargs: Optional[Dict[str, Any]] = None,
|
|
90
94
|
) -> None:
|
|
91
95
|
if args is None:
|
|
@@ -100,6 +104,8 @@ class Executor:
|
|
|
100
104
|
self.cb_bar(update_bar=1)
|
|
101
105
|
elif action == "msg_block":
|
|
102
106
|
self.cb_return.set_response(self.cb_msg_block(*args, **kwargs))
|
|
107
|
+
elif action == "msg_dynamic":
|
|
108
|
+
self.cb_msg_dynamic(*args, **kwargs)
|
|
103
109
|
elif action == "ask_bool":
|
|
104
110
|
self.cb_return.set_response(self.cb_ask_bool(*args, **kwargs))
|
|
105
111
|
elif action == "ask_str":
|
|
@@ -125,15 +131,24 @@ class Executor:
|
|
|
125
131
|
arg_dump.append("CredOption(REDACTED)")
|
|
126
132
|
else:
|
|
127
133
|
arg_dump.append(i)
|
|
128
|
-
e =
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
134
|
+
e = I(
|
|
135
|
+
"##### EXCEPTION #####\n"
|
|
136
|
+
"Function: {function}\n"
|
|
137
|
+
"Arguments: {args}\n"
|
|
138
|
+
"{tb}\n"
|
|
139
|
+
"#####################"
|
|
140
|
+
).format(
|
|
141
|
+
function=repr(work_func),
|
|
142
|
+
args=repr(arg_dump),
|
|
143
|
+
tb=traceback.format_exc(),
|
|
144
|
+
)
|
|
133
145
|
cb_queue.put(e)
|
|
134
146
|
|
|
135
147
|
work_queue.put(None)
|
|
136
148
|
cb_queue.put("__PROCESS_DONE__")
|
|
149
|
+
crd = cast(CRD, RUNTIME_STATE.get("crd"))
|
|
150
|
+
if crd:
|
|
151
|
+
crd.close()
|
|
137
152
|
|
|
138
153
|
def start_workers(self, processes: int = 1) -> None:
|
|
139
154
|
self.cb_thread_instance = Thread(
|
|
@@ -170,6 +185,12 @@ class Executor:
|
|
|
170
185
|
try:
|
|
171
186
|
for process in self.processes:
|
|
172
187
|
process.join()
|
|
188
|
+
if process.exitcode != 0 and self.is_cancel_job.value == 0:
|
|
189
|
+
self.cb_msg(
|
|
190
|
+
I("Warning: A process exited with error (code {})").format(
|
|
191
|
+
process.exitcode
|
|
192
|
+
)
|
|
193
|
+
)
|
|
173
194
|
except KeyboardInterrupt:
|
|
174
195
|
pass
|
|
175
196
|
|
|
@@ -198,32 +219,16 @@ class Job:
|
|
|
198
219
|
opt_comp: CompOption,
|
|
199
220
|
opt_output: OutputOption,
|
|
200
221
|
opt_cred: CredOption,
|
|
201
|
-
|
|
202
|
-
cb_msg_block: Callable[..., None],
|
|
203
|
-
cb_bar: Callable[..., None],
|
|
204
|
-
cb_ask_bool: Callable[..., bool],
|
|
205
|
-
cb_ask_str: Callable[..., str],
|
|
222
|
+
cb: Union[CallbackCli, CallbackGui],
|
|
206
223
|
) -> None:
|
|
207
224
|
self.opt_input = opt_input
|
|
208
225
|
self.opt_comp = opt_comp
|
|
209
226
|
self.opt_output = opt_output
|
|
210
227
|
self.opt_cred = opt_cred
|
|
211
|
-
self.cb_msg = cb_msg
|
|
212
|
-
self.cb_msg_block = cb_msg_block
|
|
213
|
-
self.cb_bar = cb_bar
|
|
214
|
-
self.cb_ask_bool = cb_ask_bool
|
|
215
|
-
self.cb_ask_str = cb_ask_str
|
|
216
228
|
|
|
217
|
-
self.compress_fails: List[str] = []
|
|
218
229
|
self.out_urls: List[str] = []
|
|
219
230
|
|
|
220
|
-
self.executor = Executor(
|
|
221
|
-
self.cb_msg,
|
|
222
|
-
self.cb_msg_block,
|
|
223
|
-
self.cb_bar,
|
|
224
|
-
self.cb_ask_bool,
|
|
225
|
-
self.cb_ask_str,
|
|
226
|
-
)
|
|
231
|
+
self.executor = Executor(cb)
|
|
227
232
|
|
|
228
233
|
def start(self) -> int:
|
|
229
234
|
if Path(self.opt_input.dir).is_dir() is False:
|
|
@@ -234,28 +239,44 @@ class Job:
|
|
|
234
239
|
|
|
235
240
|
self.executor.cb("msg", kwargs={"cls": True})
|
|
236
241
|
|
|
237
|
-
tasks = (
|
|
242
|
+
tasks: Tuple[Callable[..., Tuple[bool, Optional[str]]], ...] = (
|
|
238
243
|
self.verify_input,
|
|
239
244
|
self.cleanup,
|
|
240
245
|
self.download,
|
|
241
246
|
self.compress,
|
|
242
247
|
self.export,
|
|
243
|
-
self.report,
|
|
244
248
|
)
|
|
245
249
|
|
|
246
250
|
code = 0
|
|
251
|
+
summaries: List[str] = []
|
|
247
252
|
for task in tasks:
|
|
248
253
|
self.executor.cb("bar", kwargs={"set_progress_mode": "indeterminate"})
|
|
249
|
-
success = task()
|
|
254
|
+
success, summary = task()
|
|
255
|
+
if summary is not None:
|
|
256
|
+
summaries.append(summary)
|
|
250
257
|
|
|
251
258
|
if self.executor.is_cancel_job.value == 1: # type: ignore
|
|
252
259
|
code = 2
|
|
253
260
|
break
|
|
254
261
|
if not success:
|
|
255
262
|
code = 1
|
|
256
|
-
self.executor.cb("An error occured during this run.")
|
|
263
|
+
self.executor.cb(I("An error occured during this run."))
|
|
257
264
|
break
|
|
258
265
|
|
|
266
|
+
msg = I("##########\nSummary:\n##########\n")
|
|
267
|
+
|
|
268
|
+
msg += "\n"
|
|
269
|
+
msg += "\n".join(summaries)
|
|
270
|
+
msg += "\n"
|
|
271
|
+
|
|
272
|
+
if self.out_urls:
|
|
273
|
+
msg += I("Export results:\n")
|
|
274
|
+
msg += "\n".join(self.out_urls)
|
|
275
|
+
else:
|
|
276
|
+
msg += I("Export result: None")
|
|
277
|
+
|
|
278
|
+
self.executor.cb(msg)
|
|
279
|
+
|
|
259
280
|
self.executor.cleanup()
|
|
260
281
|
|
|
261
282
|
return code
|
|
@@ -263,49 +284,63 @@ class Job:
|
|
|
263
284
|
def cancel(self, *_: Any, **_kwargs: Any) -> None:
|
|
264
285
|
self.executor.kill_workers()
|
|
265
286
|
|
|
266
|
-
def verify_input(self) -> bool:
|
|
287
|
+
def verify_input(self) -> Tuple[bool, None]:
|
|
267
288
|
info_msg = ""
|
|
268
289
|
error_msg = ""
|
|
269
290
|
|
|
270
|
-
save_to_local_tip =
|
|
271
|
-
|
|
272
|
-
|
|
291
|
+
save_to_local_tip = I(
|
|
292
|
+
"\n"
|
|
293
|
+
" If you want to upload the results by yourself,\n"
|
|
294
|
+
' select "Save to local directory only" for output\n'
|
|
295
|
+
)
|
|
273
296
|
|
|
274
297
|
if Path(self.opt_input.dir).resolve() == Path(self.opt_output.dir).resolve():
|
|
275
|
-
error_msg += "\n"
|
|
276
|
-
error_msg += "[X] Input and output directories cannot be the same\n"
|
|
298
|
+
error_msg += I("\n[X] Input and output directories cannot be the same\n")
|
|
277
299
|
|
|
278
300
|
if self.opt_input.option == "auto":
|
|
279
|
-
error_msg += "\n"
|
|
280
|
-
error_msg += "[X] Unrecognized URL input source\n"
|
|
301
|
+
error_msg += I("\n[X] Unrecognized URL input source\n")
|
|
281
302
|
|
|
282
303
|
if self.opt_input.option != "local" and not self.opt_input.url:
|
|
283
|
-
error_msg +=
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
304
|
+
error_msg += I(
|
|
305
|
+
"\n"
|
|
306
|
+
"[X] URL address cannot be empty.\n"
|
|
307
|
+
" If you only want to use local files,\n"
|
|
308
|
+
' choose "Save to local directory only"\n'
|
|
309
|
+
' in "Input source"\n'
|
|
310
|
+
)
|
|
288
311
|
|
|
289
312
|
if (
|
|
290
313
|
self.opt_input.option == "telegram" or self.opt_output.option == "telegram"
|
|
291
314
|
) and not self.opt_cred.telegram_token:
|
|
292
|
-
error_msg += (
|
|
315
|
+
error_msg += I(
|
|
293
316
|
"[X] Downloading from and uploading to telegram requires bot token.\n"
|
|
294
317
|
)
|
|
295
318
|
error_msg += save_to_local_tip
|
|
296
319
|
|
|
320
|
+
if (
|
|
321
|
+
self.opt_input.option.startswith("discord")
|
|
322
|
+
and not self.opt_cred.discord_token
|
|
323
|
+
):
|
|
324
|
+
error_msg += I("[X] Downloading from Discord requires token.\n")
|
|
325
|
+
|
|
297
326
|
if self.opt_output.option == "telegram" and not self.opt_cred.telegram_userid:
|
|
298
|
-
error_msg +=
|
|
299
|
-
|
|
327
|
+
error_msg += I(
|
|
328
|
+
"[X] Uploading to telegram requires user_id \n"
|
|
329
|
+
" (From real account, not bot account).\n"
|
|
330
|
+
)
|
|
300
331
|
error_msg += save_to_local_tip
|
|
301
332
|
|
|
302
333
|
if self.opt_output.option == "signal" and not (
|
|
303
334
|
self.opt_cred.signal_uuid and self.opt_cred.signal_password
|
|
304
335
|
):
|
|
305
|
-
error_msg += "[X] Uploading to signal requires uuid and password.\n"
|
|
336
|
+
error_msg += I("[X] Uploading to signal requires uuid and password.\n")
|
|
337
|
+
error_msg += save_to_local_tip
|
|
338
|
+
|
|
339
|
+
if self.opt_output.option == "viber" and not self.opt_cred.viber_auth:
|
|
340
|
+
error_msg += I("[X] Uploading to Viber requires auth data.\n")
|
|
306
341
|
error_msg += save_to_local_tip
|
|
307
342
|
|
|
308
|
-
output_presets =
|
|
343
|
+
output_presets = load_resource_json("output")
|
|
309
344
|
|
|
310
345
|
input_option = self.opt_input.option
|
|
311
346
|
output_option = self.opt_output.option
|
|
@@ -317,30 +352,44 @@ class Job:
|
|
|
317
352
|
if not MetadataHandler.check_metadata_provided(
|
|
318
353
|
self.opt_input.dir, input_option, metadata
|
|
319
354
|
):
|
|
320
|
-
error_msg +=
|
|
355
|
+
error_msg += I("[X] {output_option} requires {metadata}\n").format(
|
|
356
|
+
output_option=output_presets[output_option]["full_name"],
|
|
357
|
+
metadata=metadata,
|
|
358
|
+
)
|
|
321
359
|
if self.opt_input.option == "local":
|
|
322
|
-
error_msg +=
|
|
360
|
+
error_msg += I(
|
|
361
|
+
" {metadata} was not supplied and {metadata}.txt is absent\n"
|
|
362
|
+
).format(metadata=metadata)
|
|
323
363
|
else:
|
|
324
|
-
error_msg +=
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
364
|
+
error_msg += I(
|
|
365
|
+
" {metadata} was not supplied and input source will not provide {metadata}\n"
|
|
366
|
+
).format(metadata=metadata)
|
|
367
|
+
error_msg += I(
|
|
368
|
+
" Supply the {metadata} by filling in the option, or\n"
|
|
369
|
+
" Create {metadata}.txt with the {metadata} name\n"
|
|
370
|
+
).format(metadata=metadata)
|
|
329
371
|
else:
|
|
330
|
-
info_msg +=
|
|
372
|
+
info_msg += I("[!] {output_option} requires {metadata}\n").format(
|
|
373
|
+
output_option=output_presets[output_option]["full_name"],
|
|
374
|
+
metadata=metadata,
|
|
375
|
+
)
|
|
331
376
|
if self.opt_input.option == "local":
|
|
332
|
-
info_msg +=
|
|
333
|
-
|
|
377
|
+
info_msg += I(
|
|
378
|
+
" {metadata} was not supplied but {metadata}.txt is present\n"
|
|
379
|
+
" Using {metadata} name in {metadata}.txt\n"
|
|
380
|
+
).format(metadata=metadata)
|
|
334
381
|
else:
|
|
335
|
-
info_msg +=
|
|
336
|
-
|
|
382
|
+
info_msg += I(
|
|
383
|
+
f" {metadata} was not supplied but input source will provide {metadata}\n"
|
|
384
|
+
f" Using {metadata} provided by input source\n"
|
|
385
|
+
).format(metadata=metadata)
|
|
337
386
|
|
|
338
387
|
if info_msg != "":
|
|
339
388
|
self.executor.cb(info_msg)
|
|
340
389
|
|
|
341
390
|
if error_msg != "":
|
|
342
391
|
self.executor.cb(error_msg)
|
|
343
|
-
return False
|
|
392
|
+
return False, None
|
|
344
393
|
|
|
345
394
|
# Check if preset not equal to export option
|
|
346
395
|
# Only warn if the compression option is available in export preset
|
|
@@ -350,17 +399,22 @@ class Job:
|
|
|
350
399
|
not self.opt_comp.no_compress
|
|
351
400
|
and self.opt_output.option != "local"
|
|
352
401
|
and self.opt_comp.preset != "custom"
|
|
353
|
-
and
|
|
402
|
+
and (
|
|
403
|
+
self.opt_output.option not in self.opt_comp.preset
|
|
404
|
+
and self.opt_comp.preset not in self.opt_output.option
|
|
405
|
+
)
|
|
354
406
|
):
|
|
355
|
-
msg =
|
|
356
|
-
|
|
357
|
-
|
|
407
|
+
msg = I(
|
|
408
|
+
"Compression preset does not match export option\n"
|
|
409
|
+
"You may continue, but the files will need to be compressed again before export\n"
|
|
410
|
+
"You are recommended to choose the matching option for compression and output. Continue?"
|
|
411
|
+
)
|
|
358
412
|
|
|
359
413
|
self.executor.cb("ask_bool", (msg,))
|
|
360
414
|
response = self.executor.cb_return.get_response()
|
|
361
415
|
|
|
362
416
|
if response is False:
|
|
363
|
-
return False
|
|
417
|
+
return False, None
|
|
364
418
|
|
|
365
419
|
for param, value in (
|
|
366
420
|
("fps_power", self.opt_comp.fps_power),
|
|
@@ -369,8 +423,10 @@ class Job:
|
|
|
369
423
|
("color_power", self.opt_comp.color_power),
|
|
370
424
|
):
|
|
371
425
|
if value < -1:
|
|
372
|
-
error_msg +=
|
|
373
|
-
|
|
426
|
+
error_msg += I(
|
|
427
|
+
"\n"
|
|
428
|
+
"[X] {param} should be between -1 and positive infinity. {value} was given."
|
|
429
|
+
).format(param=param, value=value)
|
|
374
430
|
|
|
375
431
|
if self.opt_comp.scale_filter not in (
|
|
376
432
|
"nearest",
|
|
@@ -380,27 +436,32 @@ class Job:
|
|
|
380
436
|
"bicubic",
|
|
381
437
|
"lanczos",
|
|
382
438
|
):
|
|
383
|
-
error_msg +=
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
)
|
|
387
|
-
error_msg += (
|
|
439
|
+
error_msg += I(
|
|
440
|
+
"\n"
|
|
441
|
+
"[X] scale_filter {scale_filter} is not valid option\n"
|
|
388
442
|
" Valid options: nearest, box, bilinear, hamming, bicubic, lanczos"
|
|
389
|
-
)
|
|
390
|
-
|
|
391
|
-
if self.opt_comp.quantize_method not in (
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
443
|
+
).format(scale_filter=self.opt_comp.scale_filter)
|
|
444
|
+
|
|
445
|
+
if self.opt_comp.quantize_method not in (
|
|
446
|
+
"imagequant",
|
|
447
|
+
"fastoctree",
|
|
448
|
+
"maxcoverage",
|
|
449
|
+
"mediancut",
|
|
450
|
+
"none",
|
|
451
|
+
):
|
|
452
|
+
I(
|
|
453
|
+
"\n"
|
|
454
|
+
"[X] quantize_method {quantize_method} is not valid option\n"
|
|
455
|
+
" Valid options: imagequant, fastoctree, maxcoverage, mediancut, none"
|
|
456
|
+
).format(quantize_method=self.opt_comp.quantize_method)
|
|
395
457
|
|
|
396
458
|
if self.opt_comp.bg_color:
|
|
397
459
|
try:
|
|
398
460
|
_, _, _ = bytes.fromhex(self.opt_comp.bg_color)
|
|
399
461
|
except ValueError:
|
|
400
|
-
error_msg +=
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
)
|
|
462
|
+
error_msg += I(
|
|
463
|
+
"\n[X] bg_color {bg_color} is not valid color hex"
|
|
464
|
+
).format(bg_color=self.opt_comp.bg_color)
|
|
404
465
|
|
|
405
466
|
# Warn about unable to download animated Kakao stickers with such link
|
|
406
467
|
if (
|
|
@@ -408,18 +469,20 @@ class Job:
|
|
|
408
469
|
and urlparse(self.opt_input.url).netloc == "e.kakao.com"
|
|
409
470
|
and not self.opt_cred.kakao_auth_token
|
|
410
471
|
):
|
|
411
|
-
msg =
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
472
|
+
msg = I(
|
|
473
|
+
"To download ANIMATED stickers from e.kakao.com,\n"
|
|
474
|
+
"you need to generate auth_token.\n"
|
|
475
|
+
"Alternatively, you can generate share link (emoticon.kakao.com/items/xxxxx)\n"
|
|
476
|
+
"from Kakao app on phone.\n"
|
|
477
|
+
"You are adviced to read documentations.\n"
|
|
478
|
+
"If you continue, you will only download static stickers. Continue?"
|
|
479
|
+
)
|
|
417
480
|
|
|
418
481
|
self.executor.cb("ask_bool", (msg,))
|
|
419
482
|
response = self.executor.cb_return.get_response()
|
|
420
483
|
|
|
421
484
|
if response is False:
|
|
422
|
-
return False
|
|
485
|
+
return False, None
|
|
423
486
|
|
|
424
487
|
# Warn about in/output directories that might contain other files
|
|
425
488
|
# Directory is safe if the name is stickers_input/stickers_output, or
|
|
@@ -443,26 +506,33 @@ class Job:
|
|
|
443
506
|
|
|
444
507
|
related_files = MetadataHandler.get_files_related_to_sticker_convert(path)
|
|
445
508
|
if any(i for i in path.iterdir() if i not in related_files):
|
|
446
|
-
msg =
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
509
|
+
msg = I(
|
|
510
|
+
"WARNING: {path_type} directory is set to {path}.\n"
|
|
511
|
+
'It does not have default name of "{default_name}",\n'
|
|
512
|
+
"and It seems like it contains PERSONAL DATA.\n"
|
|
513
|
+
"During execution, contents of this directory\n"
|
|
514
|
+
'maybe MOVED to "archive_*".\n'
|
|
515
|
+
"THIS MAY CAUSE DAMAGE TO YOUR DATA. Continue?"
|
|
516
|
+
)
|
|
452
517
|
|
|
453
518
|
self.executor.cb(
|
|
454
|
-
"ask_bool",
|
|
519
|
+
"ask_bool",
|
|
520
|
+
(
|
|
521
|
+
msg.format(
|
|
522
|
+
path_type=path_type, path=path, default_name=default_name
|
|
523
|
+
),
|
|
524
|
+
),
|
|
455
525
|
)
|
|
456
526
|
response = self.executor.cb_return.get_response()
|
|
457
527
|
|
|
458
528
|
if response is False:
|
|
459
|
-
return False
|
|
529
|
+
return False, None
|
|
460
530
|
|
|
461
531
|
break
|
|
462
532
|
|
|
463
|
-
return True
|
|
533
|
+
return True, None
|
|
464
534
|
|
|
465
|
-
def cleanup(self) -> bool:
|
|
535
|
+
def cleanup(self) -> Tuple[bool, None]:
|
|
466
536
|
# If input is 'From local directory', then we should keep files in input/output directory as it maybe edited by user
|
|
467
537
|
# If input is not 'From local directory', then we should move files in input/output directory as new files will be downloaded
|
|
468
538
|
# Output directory should be cleanup unless no_compress is true (meaning files in output directory might be edited by user)
|
|
@@ -479,16 +549,18 @@ class Job:
|
|
|
479
549
|
|
|
480
550
|
if self.opt_input.option == "local":
|
|
481
551
|
self.executor.cb(
|
|
482
|
-
"Skip moving old files in input directory as input source is local"
|
|
552
|
+
I("Skip moving old files in input directory as input source is local")
|
|
483
553
|
)
|
|
484
554
|
elif len(in_dir_files) == 0:
|
|
485
555
|
self.executor.cb(
|
|
486
|
-
"Skip moving old files in input directory as input source is empty"
|
|
556
|
+
I("Skip moving old files in input directory as input source is empty")
|
|
487
557
|
)
|
|
488
558
|
else:
|
|
489
559
|
archive_dir = Path(self.opt_input.dir, dir_name)
|
|
490
560
|
self.executor.cb(
|
|
491
|
-
|
|
561
|
+
I(
|
|
562
|
+
"Moving old files in input directory to {archive_dir} as input source is not local"
|
|
563
|
+
).format(archive_dir=archive_dir)
|
|
492
564
|
)
|
|
493
565
|
archive_dir.mkdir(exist_ok=True)
|
|
494
566
|
for old_path in in_dir_files:
|
|
@@ -497,24 +569,28 @@ class Job:
|
|
|
497
569
|
|
|
498
570
|
if self.opt_comp.no_compress:
|
|
499
571
|
self.executor.cb(
|
|
500
|
-
"Skip moving old files in output directory as no_compress is True"
|
|
572
|
+
I("Skip moving old files in output directory as no_compress is True")
|
|
501
573
|
)
|
|
502
574
|
elif len(out_dir_files) == 0:
|
|
503
575
|
self.executor.cb(
|
|
504
|
-
"Skip moving old files in output directory as output source is empty"
|
|
576
|
+
I("Skip moving old files in output directory as output source is empty")
|
|
505
577
|
)
|
|
506
578
|
else:
|
|
507
579
|
archive_dir = Path(self.opt_output.dir, dir_name)
|
|
508
|
-
self.executor.cb(
|
|
580
|
+
self.executor.cb(
|
|
581
|
+
I("Moving old files in output directory to {archive_dir}").format(
|
|
582
|
+
archive_dir=archive_dir
|
|
583
|
+
)
|
|
584
|
+
)
|
|
509
585
|
os.makedirs(archive_dir)
|
|
510
586
|
for old_path in out_dir_files:
|
|
511
587
|
new_path = Path(archive_dir, old_path.name)
|
|
512
588
|
old_path.rename(new_path)
|
|
513
589
|
|
|
514
|
-
return True
|
|
590
|
+
return True, None
|
|
515
591
|
|
|
516
|
-
def download(self) -> bool:
|
|
517
|
-
downloaders: List[Callable[...,
|
|
592
|
+
def download(self) -> Tuple[bool, str]:
|
|
593
|
+
downloaders: List[Callable[..., Tuple[int, int]]] = []
|
|
518
594
|
|
|
519
595
|
if self.opt_input.option == "signal":
|
|
520
596
|
downloaders.append(DownloadSignal.start)
|
|
@@ -522,41 +598,59 @@ class Job:
|
|
|
522
598
|
if self.opt_input.option == "line":
|
|
523
599
|
downloaders.append(DownloadLine.start)
|
|
524
600
|
|
|
525
|
-
if self.opt_input.option
|
|
601
|
+
if self.opt_input.option.startswith("telegram"):
|
|
526
602
|
downloaders.append(DownloadTelegram.start)
|
|
527
603
|
|
|
528
604
|
if self.opt_input.option == "kakao":
|
|
529
605
|
downloaders.append(DownloadKakao.start)
|
|
530
606
|
|
|
607
|
+
if self.opt_input.option == "band":
|
|
608
|
+
downloaders.append(DownloadBand.start)
|
|
609
|
+
|
|
610
|
+
if self.opt_input.option == "ogq":
|
|
611
|
+
downloaders.append(DownloadOgq.start)
|
|
612
|
+
|
|
531
613
|
if self.opt_input.option == "viber":
|
|
532
614
|
downloaders.append(DownloadViber.start)
|
|
533
615
|
|
|
616
|
+
if self.opt_input.option.startswith("discord"):
|
|
617
|
+
downloaders.append(DownloadDiscord.start)
|
|
618
|
+
|
|
534
619
|
if len(downloaders) > 0:
|
|
535
|
-
self.executor.cb("Downloading...")
|
|
620
|
+
self.executor.cb(I("Downloading..."))
|
|
536
621
|
else:
|
|
537
|
-
self.executor.cb("
|
|
538
|
-
return True
|
|
622
|
+
self.executor.cb(I("Skipped download (No files to download)"))
|
|
623
|
+
return True, I("Download: Skipped (No files to download)")
|
|
539
624
|
|
|
540
625
|
self.executor.start_workers(processes=1)
|
|
541
626
|
|
|
542
627
|
for downloader in downloaders:
|
|
543
628
|
self.executor.add_work(
|
|
544
629
|
work_func=downloader,
|
|
545
|
-
work_args=(self.opt_input
|
|
630
|
+
work_args=(self.opt_input, self.opt_cred),
|
|
546
631
|
)
|
|
547
632
|
|
|
548
633
|
self.executor.join_workers()
|
|
549
634
|
|
|
550
635
|
# Return False if any of the job returns failure
|
|
636
|
+
stickers_ok = 0
|
|
637
|
+
stickers_total = 0
|
|
638
|
+
success = True
|
|
551
639
|
for result in self.executor.results_list:
|
|
552
|
-
|
|
553
|
-
|
|
640
|
+
stickers_ok += result[0]
|
|
641
|
+
stickers_total += result[1]
|
|
642
|
+
success = (
|
|
643
|
+
success if stickers_ok == stickers_total and stickers_ok > 0 else False
|
|
644
|
+
)
|
|
554
645
|
|
|
555
|
-
return
|
|
646
|
+
return (
|
|
647
|
+
success,
|
|
648
|
+
f"Download: {stickers_ok}/{stickers_total} stickers success",
|
|
649
|
+
)
|
|
556
650
|
|
|
557
|
-
def compress(self) -> bool:
|
|
651
|
+
def compress(self) -> Tuple[bool, str]:
|
|
558
652
|
if self.opt_comp.no_compress is True:
|
|
559
|
-
self.executor.cb("no_compress is set to True
|
|
653
|
+
self.executor.cb(I("Skipped compression (no_compress is set to True)"))
|
|
560
654
|
in_dir_files = [
|
|
561
655
|
i
|
|
562
656
|
for i in sorted(self.opt_input.dir.iterdir())
|
|
@@ -569,21 +663,23 @@ class Job:
|
|
|
569
663
|
]
|
|
570
664
|
if len(in_dir_files) == 0:
|
|
571
665
|
self.executor.cb(
|
|
572
|
-
"Input directory is empty, nothing to copy to output directory"
|
|
666
|
+
I("Input directory is empty, nothing to copy to output directory")
|
|
573
667
|
)
|
|
574
668
|
elif len(out_dir_files) != 0:
|
|
575
669
|
self.executor.cb(
|
|
576
|
-
|
|
670
|
+
I(
|
|
671
|
+
"Output directory is not empty, not copying files from input directory"
|
|
672
|
+
)
|
|
577
673
|
)
|
|
578
674
|
else:
|
|
579
675
|
self.executor.cb(
|
|
580
|
-
"Output directory is empty, copying files from input directory"
|
|
676
|
+
I("Output directory is empty, copying files from input directory")
|
|
581
677
|
)
|
|
582
678
|
for i in in_dir_files:
|
|
583
679
|
src_f = Path(self.opt_input.dir, i.name)
|
|
584
680
|
dst_f = Path(self.opt_output.dir, i.name)
|
|
585
681
|
shutil.copy(src_f, dst_f)
|
|
586
|
-
return True
|
|
682
|
+
return True, "Compress: Skipped (no_compress is set to True)"
|
|
587
683
|
msg = "Compressing..."
|
|
588
684
|
|
|
589
685
|
input_dir = Path(self.opt_input.dir)
|
|
@@ -598,15 +694,20 @@ class Job:
|
|
|
598
694
|
|
|
599
695
|
if not in_f.is_file():
|
|
600
696
|
continue
|
|
601
|
-
if
|
|
697
|
+
if (
|
|
698
|
+
CodecInfo.get_file_ext(i) in (".txt", ".m4a")
|
|
699
|
+
or (
|
|
700
|
+
self.opt_comp.preset != "signal" and Path(i).stem == "cover"
|
|
701
|
+
) # Signal cover has same spec as normal sticker
|
|
702
|
+
):
|
|
602
703
|
shutil.copy(in_f, output_dir / i.name)
|
|
603
704
|
else:
|
|
604
705
|
in_fs.append(i)
|
|
605
706
|
|
|
606
707
|
in_fs_count = len(in_fs)
|
|
607
708
|
if in_fs_count == 0:
|
|
608
|
-
self.executor.cb("No files to compress")
|
|
609
|
-
return True
|
|
709
|
+
self.executor.cb(I("Skipped compression (No files to compress)"))
|
|
710
|
+
return True, "Compress: Skipped (No files to compress)"
|
|
610
711
|
|
|
611
712
|
self.executor.cb(msg)
|
|
612
713
|
self.executor.cb(
|
|
@@ -625,21 +726,35 @@ class Job:
|
|
|
625
726
|
|
|
626
727
|
self.executor.join_workers()
|
|
627
728
|
|
|
628
|
-
|
|
729
|
+
success = True
|
|
730
|
+
stickers_ok = 0
|
|
731
|
+
stickers_total = 0
|
|
732
|
+
fails: List[str] = []
|
|
629
733
|
for result in self.executor.results_list:
|
|
734
|
+
stickers_total += 1
|
|
630
735
|
if result[0] is False:
|
|
631
|
-
|
|
736
|
+
success = False
|
|
737
|
+
fails.append(str(result[1]))
|
|
738
|
+
else:
|
|
739
|
+
stickers_ok += 1
|
|
632
740
|
|
|
633
|
-
|
|
741
|
+
msg_append = ""
|
|
742
|
+
if success is False:
|
|
743
|
+
msg_append = " (Failed: " + ", ".join(fails) + ")"
|
|
634
744
|
|
|
635
|
-
|
|
745
|
+
return (
|
|
746
|
+
success,
|
|
747
|
+
f"Compress: {stickers_ok}/{stickers_total} stickers success" + msg_append,
|
|
748
|
+
)
|
|
749
|
+
|
|
750
|
+
def export(self) -> Tuple[bool, str]:
|
|
636
751
|
if self.opt_output.option == "local":
|
|
637
|
-
self.executor.cb("Saving to local directory only
|
|
638
|
-
return True
|
|
752
|
+
self.executor.cb(I("Skipped export (Saving to local directory only)"))
|
|
753
|
+
return True, I("Export: Skipped (Saving to local directory only)")
|
|
639
754
|
|
|
640
|
-
self.executor.cb("Exporting...")
|
|
755
|
+
self.executor.cb(I("Exporting..."))
|
|
641
756
|
|
|
642
|
-
exporters: List[Callable[..., List[str]]] = []
|
|
757
|
+
exporters: List[Callable[..., Tuple[int, int, List[str]]]] = []
|
|
643
758
|
|
|
644
759
|
if self.opt_output.option == "whatsapp":
|
|
645
760
|
exporters.append(CompressWastickers.start)
|
|
@@ -647,15 +762,15 @@ class Job:
|
|
|
647
762
|
if self.opt_output.option == "signal":
|
|
648
763
|
exporters.append(UploadSignal.start)
|
|
649
764
|
|
|
650
|
-
if self.opt_output.option
|
|
651
|
-
exporters.append(UploadTelegram.start)
|
|
652
|
-
|
|
653
|
-
if self.opt_output.option == "telegram_emoji":
|
|
765
|
+
if self.opt_output.option.startswith("telegram"):
|
|
654
766
|
exporters.append(UploadTelegram.start)
|
|
655
767
|
|
|
656
768
|
if self.opt_output.option == "imessage":
|
|
657
769
|
exporters.append(XcodeImessage.start)
|
|
658
770
|
|
|
771
|
+
if self.opt_output.option == "viber":
|
|
772
|
+
exporters.append(UploadViber.start)
|
|
773
|
+
|
|
659
774
|
self.executor.start_workers(processes=1)
|
|
660
775
|
|
|
661
776
|
for exporter in exporters:
|
|
@@ -666,8 +781,12 @@ class Job:
|
|
|
666
781
|
|
|
667
782
|
self.executor.join_workers()
|
|
668
783
|
|
|
784
|
+
stickers_ok = 0
|
|
785
|
+
stickers_total = 0
|
|
669
786
|
for result in self.executor.results_list:
|
|
670
|
-
|
|
787
|
+
stickers_ok += result[0]
|
|
788
|
+
stickers_total += result[1]
|
|
789
|
+
self.out_urls.extend(result[2])
|
|
671
790
|
|
|
672
791
|
if self.out_urls:
|
|
673
792
|
with open(
|
|
@@ -675,30 +794,11 @@ class Job:
|
|
|
675
794
|
) as f:
|
|
676
795
|
f.write("\n".join(self.out_urls))
|
|
677
796
|
else:
|
|
678
|
-
self.executor.cb("An error occured while exporting stickers")
|
|
679
|
-
return False
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
msg += "##########\n"
|
|
687
|
-
msg += "\n"
|
|
688
|
-
|
|
689
|
-
if self.compress_fails:
|
|
690
|
-
msg += f'Warning: Could not compress the following {len(self.compress_fails)} file{"s" if len(self.compress_fails) > 1 else ""}:\n'
|
|
691
|
-
msg += "\n".join(self.compress_fails)
|
|
692
|
-
msg += "\n"
|
|
693
|
-
msg += "\nConsider adjusting compression parameters"
|
|
694
|
-
msg += "\n"
|
|
695
|
-
|
|
696
|
-
if self.out_urls:
|
|
697
|
-
msg += "Export results:\n"
|
|
698
|
-
msg += "\n".join(self.out_urls)
|
|
699
|
-
else:
|
|
700
|
-
msg += "Export result: None"
|
|
701
|
-
|
|
702
|
-
self.executor.cb(msg)
|
|
703
|
-
|
|
704
|
-
return True
|
|
797
|
+
self.executor.cb(I("An error occured while exporting stickers"))
|
|
798
|
+
return False, I(
|
|
799
|
+
"Export: {stickers_ok}/{stickers_total} stickers success"
|
|
800
|
+
).format(stickers_ok=stickers_ok, stickers_total=stickers_total)
|
|
801
|
+
|
|
802
|
+
return True, I(
|
|
803
|
+
"Export: {stickers_ok}/{stickers_total} stickers success"
|
|
804
|
+
).format(stickers_ok=stickers_ok, stickers_total=stickers_total)
|