sticker-convert 2.12.3__py3-none-any.whl → 2.13.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/cli.py +3 -0
- sticker_convert/converter.py +61 -54
- sticker_convert/downloaders/download_band.py +110 -0
- sticker_convert/downloaders/download_kakao.py +84 -22
- sticker_convert/gui.py +14 -3
- sticker_convert/gui_components/windows/advanced_compression_window.py +17 -0
- sticker_convert/gui_components/windows/discord_get_auth_window.py +3 -3
- sticker_convert/gui_components/windows/kakao_get_auth_window.py +2 -6
- sticker_convert/gui_components/windows/signal_get_auth_window.py +3 -3
- sticker_convert/gui_components/windows/viber_get_auth_window.py +1 -1
- sticker_convert/job.py +6 -0
- sticker_convert/job_option.py +2 -0
- sticker_convert/resources/compression.json +47 -0
- sticker_convert/resources/help.json +1 -0
- sticker_convert/resources/input.json +10 -0
- sticker_convert/resources/memdump_linux.sh +0 -1
- sticker_convert/utils/auth/get_discord_auth.py +1 -1
- sticker_convert/utils/auth/get_kakao_desktop_auth.py +119 -35
- sticker_convert/utils/auth/get_signal_auth.py +2 -2
- sticker_convert/utils/auth/get_viber_auth.py +1 -1
- sticker_convert/utils/auth/telethon_setup.py +3 -1
- sticker_convert/utils/chrome_remotedebug.py +25 -13
- sticker_convert/utils/media/codec_info.py +1 -1
- sticker_convert/utils/singletons.py +18 -0
- sticker_convert/utils/url_detect.py +3 -0
- sticker_convert/version.py +1 -1
- {sticker_convert-2.12.3.dist-info → sticker_convert-2.13.0.dist-info}/METADATA +35 -27
- {sticker_convert-2.12.3.dist-info → sticker_convert-2.13.0.dist-info}/RECORD +32 -30
- {sticker_convert-2.12.3.dist-info → sticker_convert-2.13.0.dist-info}/WHEEL +1 -1
- {sticker_convert-2.12.3.dist-info → sticker_convert-2.13.0.dist-info}/entry_points.txt +0 -0
- {sticker_convert-2.12.3.dist-info → sticker_convert-2.13.0.dist-info}/licenses/LICENSE +0 -0
- {sticker_convert-2.12.3.dist-info → sticker_convert-2.13.0.dist-info}/top_level.txt +0 -0
sticker_convert/job.py
CHANGED
@@ -12,6 +12,7 @@ from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
12
12
|
from urllib.parse import urlparse
|
13
13
|
|
14
14
|
from sticker_convert.converter import StickerConvert
|
15
|
+
from sticker_convert.downloaders.download_band import DownloadBand
|
15
16
|
from sticker_convert.downloaders.download_discord import DownloadDiscord
|
16
17
|
from sticker_convert.downloaders.download_kakao import DownloadKakao
|
17
18
|
from sticker_convert.downloaders.download_line import DownloadLine
|
@@ -28,6 +29,7 @@ from sticker_convert.utils.callback import CallbackReturn, CbQueueType, ResultsL
|
|
28
29
|
from sticker_convert.utils.files.json_resources_loader import OUTPUT_JSON
|
29
30
|
from sticker_convert.utils.files.metadata_handler import MetadataHandler
|
30
31
|
from sticker_convert.utils.media.codec_info import CodecInfo
|
32
|
+
from sticker_convert.utils.singletons import singletons
|
31
33
|
|
32
34
|
|
33
35
|
class Executor:
|
@@ -136,6 +138,7 @@ class Executor:
|
|
136
138
|
|
137
139
|
work_queue.put(None)
|
138
140
|
cb_queue.put("__PROCESS_DONE__")
|
141
|
+
singletons.close()
|
139
142
|
|
140
143
|
def start_workers(self, processes: int = 1) -> None:
|
141
144
|
self.cb_thread_instance = Thread(
|
@@ -564,6 +567,9 @@ class Job:
|
|
564
567
|
if self.opt_input.option == "kakao":
|
565
568
|
downloaders.append(DownloadKakao.start)
|
566
569
|
|
570
|
+
if self.opt_input.option == "band":
|
571
|
+
downloaders.append(DownloadBand.start)
|
572
|
+
|
567
573
|
if self.opt_input.option == "viber":
|
568
574
|
downloaders.append(DownloadViber.start)
|
569
575
|
|
sticker_convert/job_option.py
CHANGED
@@ -75,6 +75,7 @@ class CompOption(BaseOption):
|
|
75
75
|
fake_vid: Optional[bool] = None
|
76
76
|
quantize_method: Optional[str] = None
|
77
77
|
scale_filter: Optional[str] = None
|
78
|
+
chromium_path: Optional[str] = None
|
78
79
|
cache_dir: Optional[str] = None
|
79
80
|
default_emoji: str = "😀"
|
80
81
|
no_compress: Optional[bool] = None
|
@@ -109,6 +110,7 @@ class CompOption(BaseOption):
|
|
109
110
|
"fake_vid": self.fake_vid,
|
110
111
|
"quantize_method": self.quantize_method,
|
111
112
|
"scale_filter": self.scale_filter,
|
113
|
+
"chromium_path": self.chromium_path,
|
112
114
|
"cache_dir": self.cache_dir,
|
113
115
|
"default_emoji": self.default_emoji,
|
114
116
|
"no_compress": self.no_compress,
|
@@ -328,6 +328,53 @@
|
|
328
328
|
"quantize_method": "imagequant",
|
329
329
|
"default_emoji": "😀"
|
330
330
|
},
|
331
|
+
"band": {
|
332
|
+
"size_max": {
|
333
|
+
"img": 0,
|
334
|
+
"vid": 0
|
335
|
+
},
|
336
|
+
"format": {
|
337
|
+
"img": ".png",
|
338
|
+
"vid": ".png"
|
339
|
+
},
|
340
|
+
"fps": {
|
341
|
+
"min": 1,
|
342
|
+
"max": 30,
|
343
|
+
"power": -0.5
|
344
|
+
},
|
345
|
+
"res": {
|
346
|
+
"w": {
|
347
|
+
"min": 100,
|
348
|
+
"max": 370
|
349
|
+
},
|
350
|
+
"h": {
|
351
|
+
"min": 100,
|
352
|
+
"max": 320
|
353
|
+
},
|
354
|
+
"power": 3
|
355
|
+
},
|
356
|
+
"quality": {
|
357
|
+
"min": 10,
|
358
|
+
"max": 95,
|
359
|
+
"power": 5
|
360
|
+
},
|
361
|
+
"color": {
|
362
|
+
"min": 32,
|
363
|
+
"max": 257,
|
364
|
+
"power": 3
|
365
|
+
},
|
366
|
+
"duration": {
|
367
|
+
"min": 83,
|
368
|
+
"max": 4000
|
369
|
+
},
|
370
|
+
"padding_percent": 0,
|
371
|
+
"bg_color": "",
|
372
|
+
"steps": 16,
|
373
|
+
"fake_vid": false,
|
374
|
+
"scale_filter": "bicubic",
|
375
|
+
"quantize_method": "imagequant",
|
376
|
+
"default_emoji": "😀"
|
377
|
+
},
|
331
378
|
"viber": {
|
332
379
|
"size_max": {
|
333
380
|
"img": 0,
|
@@ -52,6 +52,7 @@
|
|
52
52
|
"scale_filter": "Set scale filter. Default as bicubic. 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",
|
53
53
|
"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",
|
54
54
|
"cache_dir": "Set custom cache directory.\nUseful for debugging, or speed up conversion if cache_dir is on RAM disk.",
|
55
|
+
"chromium_path": "Set Chromium(-based)/Chrome browser path.\nRequired for converting from SVG files.\nLeave blank to auto detect",
|
55
56
|
"default_emoji": "Set the default emoji for uploading Signal and Telegram sticker packs."
|
56
57
|
},
|
57
58
|
"cred": {
|
@@ -59,6 +59,16 @@
|
|
59
59
|
"author": true
|
60
60
|
}
|
61
61
|
},
|
62
|
+
"band": {
|
63
|
+
"full_name": "Download from Naver Band",
|
64
|
+
"help": "Download Naver Band stickers from a URL / ID as input",
|
65
|
+
"example": "Example: https://www.band.us/sticker/xxxx OR 2535",
|
66
|
+
"address_lbls": "URL address / ID",
|
67
|
+
"metadata_provides": {
|
68
|
+
"title": true,
|
69
|
+
"author": false
|
70
|
+
}
|
71
|
+
},
|
62
72
|
"viber": {
|
63
73
|
"full_name": "Download from Viber",
|
64
74
|
"help": "Download viber stickers from a URL as input",
|
@@ -13,7 +13,7 @@ from sticker_convert.utils.process import killall
|
|
13
13
|
|
14
14
|
|
15
15
|
class GetDiscordAuth:
|
16
|
-
def __init__(self, cb_msg: Callable[..., None] = print):
|
16
|
+
def __init__(self, cb_msg: Callable[..., None] = print) -> None:
|
17
17
|
chromedriver_download_dir = CONFIG_DIR / "bin"
|
18
18
|
os.makedirs(chromedriver_download_dir, exist_ok=True)
|
19
19
|
|
@@ -18,21 +18,33 @@ then login to Kakao Desktop and try again."""
|
|
18
18
|
MSG_NO_AUTH = """Kakao Desktop installed,
|
19
19
|
but kakao_auth not found.
|
20
20
|
Please login to Kakao Desktop and try again."""
|
21
|
+
MSG_SIP_ENABLED = """You need to disable SIP:
|
22
|
+
1. Restart computer in Recovery mode
|
23
|
+
2. Launch Terminal from the Utilities menu
|
24
|
+
3. Run the command `csrutil disable`
|
25
|
+
4. Restart your computer"""
|
21
26
|
MSG_LAUNCH_FAIL = "Failed to launch Kakao"
|
22
27
|
MSG_PERMISSION_ERROR = "Failed to read Kakao process memory"
|
23
|
-
MSG_UNSUPPORTED = "Only Windows is supported for this method"
|
24
28
|
|
25
29
|
|
26
30
|
class GetKakaoDesktopAuth:
|
27
|
-
def __init__(self, cb_ask_str: Callable[..., str] = input):
|
31
|
+
def __init__(self, cb_ask_str: Callable[..., str] = input) -> None:
|
28
32
|
self.cb_ask_str = cb_ask_str
|
29
33
|
|
34
|
+
def launch_kakao(self, kakao_bin_path: str) -> None:
|
35
|
+
if platform.system() == "Windows":
|
36
|
+
subprocess.Popen([kakao_bin_path])
|
37
|
+
elif platform.system() == "Darwin":
|
38
|
+
subprocess.Popen(["open", kakao_bin_path])
|
39
|
+
else:
|
40
|
+
subprocess.Popen(["wine", kakao_bin_path])
|
41
|
+
|
30
42
|
def relaunch_kakao(self, kakao_bin_path: str) -> Optional[int]:
|
31
43
|
killed = killall("kakaotalk")
|
32
44
|
if killed:
|
33
45
|
time.sleep(5)
|
34
46
|
|
35
|
-
|
47
|
+
self.launch_kakao(kakao_bin_path)
|
36
48
|
time.sleep(20)
|
37
49
|
|
38
50
|
return find_pid_by_name("kakaotalk")
|
@@ -54,7 +66,7 @@ class GetKakaoDesktopAuth:
|
|
54
66
|
try:
|
55
67
|
with OpenProcess(pid=int(kakao_pid)) as process:
|
56
68
|
for address in process.search_by_value( # type: ignore
|
57
|
-
str, 15, "
|
69
|
+
str, 15, "authorization: "
|
58
70
|
):
|
59
71
|
auth_token_addr = cast(int, address) + 15
|
60
72
|
auth_token_bytes = process.read_process_memory(
|
@@ -127,11 +139,77 @@ class GetKakaoDesktopAuth:
|
|
127
139
|
|
128
140
|
return auth_token, msg
|
129
141
|
|
130
|
-
def
|
131
|
-
|
132
|
-
|
142
|
+
def get_auth_darwin(self, kakao_bin_path: str) -> Tuple[Optional[str], str]:
|
143
|
+
killall("kakaotalk")
|
144
|
+
|
145
|
+
subprocess.run(
|
146
|
+
[
|
147
|
+
"lldb",
|
148
|
+
kakao_bin_path,
|
149
|
+
"-o",
|
150
|
+
"b ptrace",
|
151
|
+
"-o",
|
152
|
+
"r",
|
153
|
+
"-o",
|
154
|
+
"thread return",
|
155
|
+
"-o",
|
156
|
+
"con",
|
157
|
+
"-o",
|
158
|
+
"process save-core /tmp/memdump.kakaotalk.dmp",
|
159
|
+
"-o",
|
160
|
+
"con",
|
161
|
+
"-o",
|
162
|
+
"quit",
|
163
|
+
],
|
164
|
+
stdout=subprocess.DEVNULL,
|
165
|
+
stderr=subprocess.DEVNULL,
|
133
166
|
)
|
134
|
-
|
167
|
+
|
168
|
+
with open("/tmp/memdump.kakaotalk.dmp", "rb") as f:
|
169
|
+
mem = f.read()
|
170
|
+
|
171
|
+
os.remove("/tmp/memdump.kakaotalk.dmp")
|
172
|
+
|
173
|
+
auth_token = None
|
174
|
+
for i in re.finditer(b"]mac/", mem):
|
175
|
+
auth_token_term = i.start()
|
176
|
+
|
177
|
+
auth_token_bytes = mem[auth_token_term - 200 : auth_token_term]
|
178
|
+
auth_token_start = auth_token_bytes.find(b"application/json_\x10\x8a") + 19
|
179
|
+
if auth_token_start == -1:
|
180
|
+
continue
|
181
|
+
try:
|
182
|
+
auth_token_candidate = auth_token_bytes[auth_token_start:].decode(
|
183
|
+
encoding="ascii"
|
184
|
+
)
|
185
|
+
except UnicodeDecodeError:
|
186
|
+
continue
|
187
|
+
|
188
|
+
if 150 > len(auth_token_candidate) > 100:
|
189
|
+
auth_token = auth_token_candidate
|
190
|
+
break
|
191
|
+
|
192
|
+
if auth_token is None:
|
193
|
+
return None, MSG_NO_AUTH
|
194
|
+
else:
|
195
|
+
msg = "Got auth_token successfully:\n"
|
196
|
+
msg += f"{auth_token=}\n"
|
197
|
+
|
198
|
+
return auth_token, msg
|
199
|
+
|
200
|
+
def get_kakao_desktop(self) -> Optional[str]:
|
201
|
+
if platform.system() == "Windows":
|
202
|
+
kakao_bin_path = os.path.expandvars(
|
203
|
+
"%programfiles(x86)%\\Kakao\\KakaoTalk\\KakaoTalk.exe"
|
204
|
+
)
|
205
|
+
elif platform.system() == "Darwin":
|
206
|
+
kakao_bin_path = "/Applications/KakaoTalk.app"
|
207
|
+
else:
|
208
|
+
kakao_bin_path = os.path.expanduser(
|
209
|
+
"~/.wine/drive_c/Program Files (x86)/Kakao/KakaoTalk/KakaoTalk.exe"
|
210
|
+
)
|
211
|
+
|
212
|
+
if Path(kakao_bin_path).exists():
|
135
213
|
return kakao_bin_path
|
136
214
|
|
137
215
|
return None
|
@@ -140,15 +218,6 @@ class GetKakaoDesktopAuth:
|
|
140
218
|
self,
|
141
219
|
kakao_bin_path: Optional[str] = None,
|
142
220
|
) -> Tuple[Optional[str], str]:
|
143
|
-
if platform.system() != "Windows":
|
144
|
-
return None, MSG_UNSUPPORTED
|
145
|
-
|
146
|
-
if not kakao_bin_path:
|
147
|
-
kakao_bin_path = self.get_kakao_desktop()
|
148
|
-
|
149
|
-
if not kakao_bin_path:
|
150
|
-
return None, MSG_NO_BIN
|
151
|
-
|
152
221
|
# get_auth_by_dump()
|
153
222
|
# + Fast
|
154
223
|
# - Requires admin
|
@@ -158,23 +227,38 @@ class GetKakaoDesktopAuth:
|
|
158
227
|
# - Slow
|
159
228
|
# - Cannot run on macOS
|
160
229
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
230
|
+
if not kakao_bin_path:
|
231
|
+
kakao_bin_path = self.get_kakao_desktop()
|
232
|
+
|
233
|
+
if not kakao_bin_path:
|
234
|
+
return None, MSG_NO_BIN
|
235
|
+
|
236
|
+
if platform.system() != "Darwin":
|
237
|
+
# If admin, prefer get_auth_by_dump() over get_auth_by_pme(), vice versa
|
238
|
+
methods: List[Callable[[str, bool], Tuple[Optional[str], str]]] = []
|
239
|
+
relaunch = True
|
240
|
+
kakao_auth = None
|
241
|
+
msg = ""
|
242
|
+
|
243
|
+
pme_present = importlib.util.find_spec("PyMemoryEditor") is not None
|
244
|
+
methods.append(self.get_auth_by_dump)
|
245
|
+
if pme_present:
|
246
|
+
methods.append(self.get_auth_by_pme)
|
247
|
+
if check_admin() is False:
|
248
|
+
methods.reverse()
|
249
|
+
|
250
|
+
for method in methods:
|
251
|
+
kakao_auth, msg = method(kakao_bin_path, relaunch)
|
252
|
+
relaunch = False
|
253
|
+
if kakao_auth is not None:
|
254
|
+
break
|
255
|
+
else:
|
256
|
+
csrutil_status = subprocess.run(
|
257
|
+
["csrutil", "status"], capture_output=True, text=True
|
258
|
+
).stdout
|
259
|
+
|
260
|
+
if "enabled" in csrutil_status:
|
261
|
+
return None, MSG_SIP_ENABLED
|
262
|
+
kakao_auth, msg = self.get_auth_darwin(kakao_bin_path)
|
179
263
|
|
180
264
|
return kakao_auth, msg
|
@@ -17,7 +17,7 @@ class GetSignalAuth:
|
|
17
17
|
self,
|
18
18
|
cb_msg: Callable[..., None] = print,
|
19
19
|
cb_ask_str: Callable[..., str] = input,
|
20
|
-
):
|
20
|
+
) -> None:
|
21
21
|
chromedriver_download_dir = CONFIG_DIR / "bin"
|
22
22
|
os.makedirs(chromedriver_download_dir, exist_ok=True)
|
23
23
|
|
@@ -26,7 +26,7 @@ class GetSignalAuth:
|
|
26
26
|
self.cb_ask_str = cb_ask_str
|
27
27
|
self.cb_msg = cb_msg
|
28
28
|
|
29
|
-
def download_signal_desktop(self):
|
29
|
+
def download_signal_desktop(self) -> None:
|
30
30
|
download_url = "https://signal.org/en/download/"
|
31
31
|
|
32
32
|
webbrowser.open(download_url)
|
@@ -28,7 +28,7 @@ MSG_PERMISSION_ERROR = "Failed to read Viber process memory"
|
|
28
28
|
|
29
29
|
|
30
30
|
class GetViberAuth:
|
31
|
-
def __init__(self, cb_ask_str: Callable[..., str] = input):
|
31
|
+
def __init__(self, cb_ask_str: Callable[..., str] = input) -> None:
|
32
32
|
self.cb_ask_str = cb_ask_str
|
33
33
|
|
34
34
|
def relaunch_viber(self, viber_bin_path: str) -> Optional[int]:
|
@@ -22,7 +22,9 @@ Continue when done"""
|
|
22
22
|
|
23
23
|
|
24
24
|
class TelethonSetup:
|
25
|
-
def __init__(
|
25
|
+
def __init__(
|
26
|
+
self, opt_cred: CredOption, cb_ask_str: Callable[..., str] = input
|
27
|
+
) -> None:
|
26
28
|
self.cb_ask_str = cb_ask_str
|
27
29
|
self.opt_cred = opt_cred
|
28
30
|
|
@@ -5,12 +5,12 @@ import json
|
|
5
5
|
import os
|
6
6
|
import platform
|
7
7
|
import shutil
|
8
|
+
import signal
|
8
9
|
import socket
|
9
10
|
import subprocess
|
10
11
|
import time
|
11
12
|
from typing import Any, Dict, List, Optional, Tuple, Union, cast
|
12
13
|
|
13
|
-
import browsers # type: ignore
|
14
14
|
import requests
|
15
15
|
import websocket
|
16
16
|
from PIL import Image
|
@@ -50,7 +50,7 @@ class CRD:
|
|
50
50
|
chrome_bin: str,
|
51
51
|
port: Optional[int] = None,
|
52
52
|
args: Optional[List[str]] = None,
|
53
|
-
):
|
53
|
+
) -> None:
|
54
54
|
if port is None:
|
55
55
|
port = get_free_port()
|
56
56
|
self.port = port
|
@@ -84,6 +84,14 @@ class CRD:
|
|
84
84
|
|
85
85
|
@staticmethod
|
86
86
|
def get_chrome_path() -> Optional[str]:
|
87
|
+
import logging
|
88
|
+
|
89
|
+
import browsers # type: ignore
|
90
|
+
|
91
|
+
# browsers module would turn on info logging
|
92
|
+
logger = logging.getLogger()
|
93
|
+
logger.setLevel(logging.CRITICAL)
|
94
|
+
|
87
95
|
bs: List[Tuple[int, str]] = []
|
88
96
|
for b in browsers.browsers():
|
89
97
|
browser_type = b["browser_type"]
|
@@ -98,7 +106,7 @@ class CRD:
|
|
98
106
|
bs = sorted(bs, key=lambda x: x[0])
|
99
107
|
return bs[0][1]
|
100
108
|
|
101
|
-
def connect(self, target_id: int = 0):
|
109
|
+
def connect(self, target_id: int = 0) -> None:
|
102
110
|
self.cmd_id = 1
|
103
111
|
r = None
|
104
112
|
targets: List[Any] = []
|
@@ -137,7 +145,7 @@ class CRD:
|
|
137
145
|
|
138
146
|
raise RuntimeError("Websocket keep disconnecting")
|
139
147
|
|
140
|
-
def exec_js(self, js: str, context_id: Optional[int] = None):
|
148
|
+
def exec_js(self, js: str, context_id: Optional[int] = None) -> Union[str, bytes]:
|
141
149
|
command: Dict[str, Any] = {
|
142
150
|
"id": self.cmd_id,
|
143
151
|
"method": "Runtime.evaluate",
|
@@ -155,11 +163,11 @@ class CRD:
|
|
155
163
|
}
|
156
164
|
return self.send_cmd(command)
|
157
165
|
|
158
|
-
def screenshot(self, clip: Optional[Dict[str, int]] = None):
|
166
|
+
def screenshot(self, clip: Optional[Dict[str, int]] = None) -> Image.Image:
|
159
167
|
command: Dict[str, Any] = {
|
160
168
|
"id": self.cmd_id,
|
161
169
|
"method": "Page.captureScreenshot",
|
162
|
-
"params": {},
|
170
|
+
"params": {"captureBeyondViewport": True, "optimizeForSpeed": True},
|
163
171
|
}
|
164
172
|
if clip:
|
165
173
|
command["params"]["clip"] = clip
|
@@ -174,11 +182,11 @@ class CRD:
|
|
174
182
|
str, json.loads(r).get("result", {}).get("result", {}).get("value", "")
|
175
183
|
)
|
176
184
|
|
177
|
-
def navigate(self, url: str):
|
185
|
+
def navigate(self, url: str) -> None:
|
178
186
|
command = {"id": self.cmd_id, "method": "Page.navigate", "params": {"url": url}}
|
179
187
|
self.send_cmd(command)
|
180
188
|
|
181
|
-
def open_html_str(self, html: str):
|
189
|
+
def open_html_str(self, html: str) -> None:
|
182
190
|
command: Dict[str, Any] = {
|
183
191
|
"id": self.cmd_id,
|
184
192
|
"method": "Page.navigate",
|
@@ -198,24 +206,28 @@ class CRD:
|
|
198
206
|
}
|
199
207
|
self.send_cmd(command)
|
200
208
|
|
201
|
-
def runtime_enable(self):
|
209
|
+
def runtime_enable(self) -> None:
|
202
210
|
command = {
|
203
211
|
"method": "Runtime.enable",
|
204
212
|
}
|
205
213
|
self.send_cmd(command)
|
206
214
|
|
207
|
-
def runtime_disable(self):
|
215
|
+
def runtime_disable(self) -> None:
|
208
216
|
command = {
|
209
217
|
"method": "Runtime.disable",
|
210
218
|
}
|
211
219
|
self.send_cmd(command)
|
212
220
|
|
213
|
-
def reload(self):
|
221
|
+
def reload(self) -> None:
|
214
222
|
command = {
|
215
223
|
"method": "Page.reload",
|
216
224
|
}
|
217
225
|
self.send_cmd(command)
|
218
226
|
|
219
|
-
def close(self):
|
227
|
+
def close(self) -> None:
|
228
|
+
command = {
|
229
|
+
"method": "Browser.close",
|
230
|
+
}
|
231
|
+
self.send_cmd(command)
|
220
232
|
self.ws.close()
|
221
|
-
self.chrome_proc.
|
233
|
+
os.kill(self.chrome_proc.pid, signal.SIGTERM)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
from typing import Any, Dict, Protocol
|
3
|
+
|
4
|
+
|
5
|
+
class SingletonProtocol(Protocol):
|
6
|
+
def close(self) -> Any: ...
|
7
|
+
|
8
|
+
|
9
|
+
class Singletons:
|
10
|
+
def __init__(self) -> None:
|
11
|
+
self.objs: Dict[str, SingletonProtocol] = {}
|
12
|
+
|
13
|
+
def close(self) -> None:
|
14
|
+
for obj in self.objs.values():
|
15
|
+
obj.close()
|
16
|
+
|
17
|
+
|
18
|
+
singletons = Singletons()
|
sticker_convert/version.py
CHANGED