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
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
+
from functools import partial
|
|
3
|
+
from getpass import getpass
|
|
2
4
|
from multiprocessing import Event, Manager
|
|
3
5
|
from multiprocessing.managers import ListProxy, SyncManager
|
|
4
6
|
from queue import Queue
|
|
@@ -19,6 +21,9 @@ if TYPE_CHECKING:
|
|
|
19
21
|
ResponseListType = ListProxy[ResponseItemType] # type: ignore
|
|
20
22
|
CbQueueType = Queue[CbQueueItemType] # type: ignore
|
|
21
23
|
WorkQueueType = Queue[WorkQueueItemType] # type: ignore
|
|
24
|
+
from ttkbootstrap import Toplevel # type: ignore
|
|
25
|
+
|
|
26
|
+
from sticker_convert.gui import GUI # type: ignore
|
|
22
27
|
else:
|
|
23
28
|
ResultsListType = List[Any]
|
|
24
29
|
ResponseListType = List[ResponseItemType]
|
|
@@ -51,12 +56,13 @@ class CallbackProtocol(Protocol):
|
|
|
51
56
|
def put(self, i: Union[CbQueueItemType, str]) -> Any: ...
|
|
52
57
|
|
|
53
58
|
|
|
54
|
-
class
|
|
59
|
+
class CallbackCli(CallbackProtocol):
|
|
55
60
|
def __init__(
|
|
56
61
|
self,
|
|
57
62
|
msg: Optional[Callable[..., None]] = None,
|
|
58
63
|
bar: Optional[Callable[..., None]] = None,
|
|
59
64
|
msg_block: Optional[Callable[..., None]] = None,
|
|
65
|
+
msg_dynamic: Optional[Callable[..., bool]] = None,
|
|
60
66
|
ask_bool: Optional[Callable[..., bool]] = None,
|
|
61
67
|
ask_str: Optional[Callable[..., str]] = None,
|
|
62
68
|
silent: bool = False,
|
|
@@ -80,6 +86,11 @@ class Callback(CallbackProtocol):
|
|
|
80
86
|
else:
|
|
81
87
|
self.msg_block = self.cb_msg_block
|
|
82
88
|
|
|
89
|
+
if msg_dynamic:
|
|
90
|
+
self.msg_dynamic = msg_dynamic
|
|
91
|
+
else:
|
|
92
|
+
self.msg_dynamic = self.cb_msg_dynamic
|
|
93
|
+
|
|
83
94
|
if ask_bool:
|
|
84
95
|
self.ask_bool = ask_bool
|
|
85
96
|
else:
|
|
@@ -136,6 +147,16 @@ class Callback(CallbackProtocol):
|
|
|
136
147
|
if not self.no_confirm:
|
|
137
148
|
input("Press Enter to continue...")
|
|
138
149
|
|
|
150
|
+
def cb_msg_dynamic(self, *args: Any) -> bool:
|
|
151
|
+
message = args[0]
|
|
152
|
+
|
|
153
|
+
if message is None:
|
|
154
|
+
print()
|
|
155
|
+
else:
|
|
156
|
+
print(message, end="\r", flush=True)
|
|
157
|
+
|
|
158
|
+
return True
|
|
159
|
+
|
|
139
160
|
def cb_ask_bool(self, *args: Any, **kwargs: Any) -> bool:
|
|
140
161
|
question = args[0]
|
|
141
162
|
|
|
@@ -158,17 +179,23 @@ class Callback(CallbackProtocol):
|
|
|
158
179
|
|
|
159
180
|
def cb_ask_str(
|
|
160
181
|
self,
|
|
161
|
-
|
|
182
|
+
question: Optional[str] = None,
|
|
162
183
|
initialvalue: Optional[str] = None,
|
|
163
184
|
cli_show_initialvalue: bool = True,
|
|
185
|
+
password: bool = False,
|
|
164
186
|
) -> str:
|
|
165
|
-
self.msg(
|
|
187
|
+
self.msg(question)
|
|
166
188
|
|
|
167
189
|
hint = ""
|
|
168
190
|
if cli_show_initialvalue and initialvalue:
|
|
169
191
|
hint = f" [Default: {initialvalue}]"
|
|
170
192
|
|
|
171
|
-
|
|
193
|
+
if password is True:
|
|
194
|
+
if question is None:
|
|
195
|
+
question = "Password: "
|
|
196
|
+
response = getpass(question)
|
|
197
|
+
else:
|
|
198
|
+
response = input(f"Enter your response and press enter{hint} > ")
|
|
172
199
|
|
|
173
200
|
if initialvalue and not response:
|
|
174
201
|
response = initialvalue
|
|
@@ -179,7 +206,210 @@ class Callback(CallbackProtocol):
|
|
|
179
206
|
if isinstance(i, tuple):
|
|
180
207
|
action = i[0]
|
|
181
208
|
if len(i) >= 2:
|
|
182
|
-
args: Tuple[
|
|
209
|
+
args: Tuple[Any, ...] = i[1] if i[1] else tuple()
|
|
210
|
+
else:
|
|
211
|
+
args = tuple()
|
|
212
|
+
if len(i) >= 3:
|
|
213
|
+
kwargs: Dict[str, Any] = i[2] if i[2] else {}
|
|
214
|
+
else:
|
|
215
|
+
kwargs = {}
|
|
216
|
+
else:
|
|
217
|
+
action = i
|
|
218
|
+
args = tuple()
|
|
219
|
+
kwargs = {}
|
|
220
|
+
|
|
221
|
+
# Fake implementation for Queue.put()
|
|
222
|
+
if action is None:
|
|
223
|
+
return None
|
|
224
|
+
if action == "msg":
|
|
225
|
+
self.msg(*args, **kwargs)
|
|
226
|
+
elif action == "bar":
|
|
227
|
+
self.bar(**kwargs)
|
|
228
|
+
elif action == "update_bar":
|
|
229
|
+
self.bar(update_bar=1)
|
|
230
|
+
elif action == "msg_block":
|
|
231
|
+
return self.msg_block(*args, **kwargs)
|
|
232
|
+
elif action == "msg_dynamic":
|
|
233
|
+
return self.msg_dynamic(*args, **kwargs)
|
|
234
|
+
elif action == "ask_bool":
|
|
235
|
+
return self.ask_bool(*args, **kwargs)
|
|
236
|
+
elif action == "ask_str":
|
|
237
|
+
return self.ask_str(*args, **kwargs)
|
|
238
|
+
else:
|
|
239
|
+
self.msg(action)
|
|
240
|
+
return None
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class CallbackGui(CallbackProtocol):
|
|
244
|
+
def __init__(self, gui: "GUI"):
|
|
245
|
+
from ttkbootstrap import Label, Progressbar, StringVar # type: ignore
|
|
246
|
+
|
|
247
|
+
from sticker_convert.gui_components.windows.base_window import BaseWindow
|
|
248
|
+
from sticker_convert.job import Job
|
|
249
|
+
|
|
250
|
+
self.gui = gui
|
|
251
|
+
self.job: Optional[Job] = None
|
|
252
|
+
self.msg_dynamic_window: Optional[BaseWindow] = None
|
|
253
|
+
self.message_var = StringVar(self.gui)
|
|
254
|
+
self.msg_dynamic_label: Optional[Label] = None
|
|
255
|
+
self.msg_dynamic_bar: Optional[Progressbar] = None
|
|
256
|
+
|
|
257
|
+
self.ask_str = self.cb_ask_str
|
|
258
|
+
self.ask_bool = self.cb_ask_bool
|
|
259
|
+
self.msg = self.cb_msg
|
|
260
|
+
self.msg_block = self.cb_msg_block
|
|
261
|
+
self.msg_dynamic = self.cb_msg_dynamic
|
|
262
|
+
self.bar = self.cb_bar
|
|
263
|
+
|
|
264
|
+
def cb_ask_str(
|
|
265
|
+
self,
|
|
266
|
+
question: str,
|
|
267
|
+
initialvalue: Optional[str] = None,
|
|
268
|
+
cli_show_initialvalue: bool = True,
|
|
269
|
+
password: bool = False,
|
|
270
|
+
parent: Optional[object] = None,
|
|
271
|
+
) -> str:
|
|
272
|
+
from ttkbootstrap import Querybox # type: ignore
|
|
273
|
+
|
|
274
|
+
self.gui.action = partial(
|
|
275
|
+
Querybox.get_string, # type: ignore
|
|
276
|
+
question,
|
|
277
|
+
title="sticker-convert",
|
|
278
|
+
initialvalue=initialvalue,
|
|
279
|
+
parent=parent,
|
|
280
|
+
)
|
|
281
|
+
self.gui.event_generate("<<exec_in_main>>")
|
|
282
|
+
self.gui.response_event.wait()
|
|
283
|
+
self.gui.response_event.clear()
|
|
284
|
+
|
|
285
|
+
if self.gui.response is None:
|
|
286
|
+
return ""
|
|
287
|
+
elif isinstance(self.gui.response, str):
|
|
288
|
+
return self.gui.response
|
|
289
|
+
else:
|
|
290
|
+
raise RuntimeError(f"Invalid response in cb_ask_str: {self.gui.response}")
|
|
291
|
+
|
|
292
|
+
def cb_ask_bool(self, question: str, parent: Optional["Toplevel"] = None) -> bool:
|
|
293
|
+
from ttkbootstrap.dialogs import Messagebox # type: ignore
|
|
294
|
+
|
|
295
|
+
self.gui.action = partial(
|
|
296
|
+
Messagebox.yesno, # type: ignore
|
|
297
|
+
question,
|
|
298
|
+
title="sticker-convert",
|
|
299
|
+
parent=parent,
|
|
300
|
+
)
|
|
301
|
+
self.gui.event_generate("<<exec_in_main>>")
|
|
302
|
+
self.gui.response_event.wait()
|
|
303
|
+
self.gui.response_event.clear()
|
|
304
|
+
|
|
305
|
+
if self.gui.response == "Yes":
|
|
306
|
+
return True
|
|
307
|
+
return False
|
|
308
|
+
|
|
309
|
+
def cb_msg(self, *args: Any, **kwargs: Any) -> None:
|
|
310
|
+
self.gui.progress_frame.update_message_box(*args, **kwargs)
|
|
311
|
+
|
|
312
|
+
def cb_msg_block(
|
|
313
|
+
self,
|
|
314
|
+
message: Optional[str] = None,
|
|
315
|
+
parent: Optional[object] = None,
|
|
316
|
+
**_kwargs: Any,
|
|
317
|
+
) -> Any:
|
|
318
|
+
from ttkbootstrap.dialogs import Messagebox # type: ignore
|
|
319
|
+
|
|
320
|
+
self.gui.action = partial(
|
|
321
|
+
Messagebox.show_info, # type: ignore
|
|
322
|
+
message,
|
|
323
|
+
title="sticker-convert",
|
|
324
|
+
parent=parent,
|
|
325
|
+
)
|
|
326
|
+
self.gui.event_generate("<<exec_in_main>>")
|
|
327
|
+
self.gui.response_event.wait()
|
|
328
|
+
self.gui.response_event.clear()
|
|
329
|
+
|
|
330
|
+
return self.gui.response
|
|
331
|
+
|
|
332
|
+
def cb_msg_dynamic(
|
|
333
|
+
self,
|
|
334
|
+
message: Optional[str] = None,
|
|
335
|
+
parent: Optional["Toplevel"] = None,
|
|
336
|
+
**_kwargs: Any,
|
|
337
|
+
) -> bool:
|
|
338
|
+
from ttkbootstrap import Label, Progressbar # type: ignore
|
|
339
|
+
|
|
340
|
+
from sticker_convert.gui_components.gui_utils import GUIUtils
|
|
341
|
+
from sticker_convert.gui_components.windows.base_window import BaseWindow
|
|
342
|
+
|
|
343
|
+
self.gui.action = None
|
|
344
|
+
if self.msg_dynamic_window is None:
|
|
345
|
+
if message is not None:
|
|
346
|
+
self.msg_dynamic_window = BaseWindow(self.gui, parent)
|
|
347
|
+
self.msg_dynamic_window.wm_title("sticker-convert")
|
|
348
|
+
self.message_var.set(message)
|
|
349
|
+
self.msg_dynamic_label = Label(
|
|
350
|
+
self.msg_dynamic_window.scrollable_frame,
|
|
351
|
+
textvariable=self.message_var,
|
|
352
|
+
)
|
|
353
|
+
self.msg_dynamic_bar = Progressbar(
|
|
354
|
+
self.msg_dynamic_window.scrollable_frame,
|
|
355
|
+
orient="horizontal",
|
|
356
|
+
mode="indeterminate",
|
|
357
|
+
)
|
|
358
|
+
self.msg_dynamic_bar.start(50)
|
|
359
|
+
self.msg_dynamic_label.pack()
|
|
360
|
+
self.msg_dynamic_bar.pack(fill="x")
|
|
361
|
+
self.gui.action = partial(
|
|
362
|
+
GUIUtils.finalize_window, self.msg_dynamic_window
|
|
363
|
+
)
|
|
364
|
+
print(message, end="\r", flush=True)
|
|
365
|
+
ret = True
|
|
366
|
+
else:
|
|
367
|
+
print()
|
|
368
|
+
ret = False
|
|
369
|
+
elif self.msg_dynamic_window.winfo_exists() == 0:
|
|
370
|
+
self.msg_dynamic_window = None
|
|
371
|
+
print()
|
|
372
|
+
ret = False
|
|
373
|
+
else:
|
|
374
|
+
if message is None:
|
|
375
|
+
|
|
376
|
+
def _action():
|
|
377
|
+
assert self.msg_dynamic_window is not None
|
|
378
|
+
self.msg_dynamic_window.destroy()
|
|
379
|
+
self.msg_dynamic_window = None
|
|
380
|
+
|
|
381
|
+
self.gui.action = _action
|
|
382
|
+
print()
|
|
383
|
+
ret = False
|
|
384
|
+
else:
|
|
385
|
+
self.gui.action = partial(self.message_var.set, message)
|
|
386
|
+
print(message, end="\r", flush=True)
|
|
387
|
+
ret = True
|
|
388
|
+
|
|
389
|
+
if self.gui.action is not None:
|
|
390
|
+
self.gui.event_generate("<<exec_in_main>>")
|
|
391
|
+
self.gui.response_event.wait()
|
|
392
|
+
self.gui.response_event.clear()
|
|
393
|
+
|
|
394
|
+
return ret
|
|
395
|
+
|
|
396
|
+
def cb_bar(
|
|
397
|
+
self,
|
|
398
|
+
set_progress_mode: Optional[str] = None,
|
|
399
|
+
steps: int = 0,
|
|
400
|
+
update_bar: int = 0,
|
|
401
|
+
*args: Any,
|
|
402
|
+
**kwargs: Any,
|
|
403
|
+
) -> None:
|
|
404
|
+
self.gui.progress_frame.update_progress_bar(
|
|
405
|
+
set_progress_mode, steps, update_bar, *args, **kwargs
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
def put(self, i: Union[CbQueueItemType, str]) -> Union[str, bool, None]:
|
|
409
|
+
if isinstance(i, tuple):
|
|
410
|
+
action = i[0]
|
|
411
|
+
if len(i) >= 2:
|
|
412
|
+
args: Tuple[Any, ...] = i[1] if i[1] else tuple()
|
|
183
413
|
else:
|
|
184
414
|
args = tuple()
|
|
185
415
|
if len(i) >= 3:
|
|
@@ -202,10 +432,12 @@ class Callback(CallbackProtocol):
|
|
|
202
432
|
self.bar(update_bar=1)
|
|
203
433
|
elif action == "msg_block":
|
|
204
434
|
return self.msg_block(*args, **kwargs)
|
|
435
|
+
elif action == "msg_dynamic":
|
|
436
|
+
return self.msg_dynamic(*args, **kwargs)
|
|
205
437
|
elif action == "ask_bool":
|
|
206
438
|
return self.ask_bool(*args, **kwargs)
|
|
207
439
|
elif action == "ask_str":
|
|
208
|
-
return self.ask_str(**kwargs)
|
|
440
|
+
return self.ask_str(*args, **kwargs)
|
|
209
441
|
else:
|
|
210
442
|
self.msg(action)
|
|
211
443
|
return None
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import base64
|
|
3
|
+
import io
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import platform
|
|
7
|
+
import shutil
|
|
8
|
+
import signal
|
|
9
|
+
import socket
|
|
10
|
+
import subprocess
|
|
11
|
+
import time
|
|
12
|
+
from typing import Any, Dict, List, Optional, Union, cast
|
|
13
|
+
|
|
14
|
+
import requests
|
|
15
|
+
import websocket
|
|
16
|
+
from PIL import Image
|
|
17
|
+
|
|
18
|
+
# References
|
|
19
|
+
# https://github.com/yeongbin-jo/python-chromedriver-autoinstaller/blob/master/chromedriver_autoinstaller/utils.py
|
|
20
|
+
# https://chromedevtools.github.io/devtools-protocol/
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
BROWSER_PREF = [
|
|
24
|
+
"chrome",
|
|
25
|
+
"chrome-canary",
|
|
26
|
+
"chromium",
|
|
27
|
+
"msedge",
|
|
28
|
+
"msedge-beta",
|
|
29
|
+
"msedge-dev",
|
|
30
|
+
"msedge-canary",
|
|
31
|
+
"brave",
|
|
32
|
+
"brave-beta",
|
|
33
|
+
"brave-nightly",
|
|
34
|
+
"opera",
|
|
35
|
+
"opera-beta",
|
|
36
|
+
"opera-developer",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_free_port() -> int:
|
|
41
|
+
with socket.socket() as sock:
|
|
42
|
+
sock.bind(("", 0))
|
|
43
|
+
port = sock.getsockname()[1]
|
|
44
|
+
return port
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class CRD:
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
chrome_bin: str,
|
|
51
|
+
port: Optional[int] = None,
|
|
52
|
+
args: Optional[List[str]] = None,
|
|
53
|
+
) -> None:
|
|
54
|
+
if port is None:
|
|
55
|
+
port = get_free_port()
|
|
56
|
+
self.port = port
|
|
57
|
+
|
|
58
|
+
launch_cmd: List[str] = []
|
|
59
|
+
if (
|
|
60
|
+
platform.system() == "Linux"
|
|
61
|
+
and os.environ.get("DISPLAY", False) is False
|
|
62
|
+
and shutil.which("xvfb-run")
|
|
63
|
+
):
|
|
64
|
+
launch_cmd += ["xvfb-run", "--server-args='-screen 0, 1024x768x24'"]
|
|
65
|
+
|
|
66
|
+
launch_cmd += [
|
|
67
|
+
chrome_bin,
|
|
68
|
+
f"--remote-debugging-port={port}",
|
|
69
|
+
f"--remote-allow-origins=http://127.0.0.1:{port}",
|
|
70
|
+
]
|
|
71
|
+
if args:
|
|
72
|
+
launch_cmd += args
|
|
73
|
+
|
|
74
|
+
# Adding --no-sandbox in Windows may cause Signal fail to launch
|
|
75
|
+
# https://github.com/laggykiller/sticker-convert/issues/274
|
|
76
|
+
if (
|
|
77
|
+
platform.system() != "Windows"
|
|
78
|
+
and "geteuid" in dir(os)
|
|
79
|
+
and os.geteuid() == 0
|
|
80
|
+
) or os.path.isfile("/.dockerenv"):
|
|
81
|
+
launch_cmd.append("--no-sandbox")
|
|
82
|
+
|
|
83
|
+
self.chrome_proc = subprocess.Popen(launch_cmd)
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def get_chromium_path() -> Optional[str]:
|
|
87
|
+
if platform.system() == "Windows":
|
|
88
|
+
from sticker_convert.utils.chromiums.windows import get_chromium_path
|
|
89
|
+
elif platform.system() == "Darwin":
|
|
90
|
+
from sticker_convert.utils.chromiums.osx import get_chromium_path
|
|
91
|
+
else:
|
|
92
|
+
from sticker_convert.utils.chromiums.linux import get_chromium_path
|
|
93
|
+
return get_chromium_path()
|
|
94
|
+
|
|
95
|
+
def connect(self, target_id: int = 0) -> None:
|
|
96
|
+
self.cmd_id = 1
|
|
97
|
+
r = None
|
|
98
|
+
targets: List[Any] = []
|
|
99
|
+
for _ in range(30):
|
|
100
|
+
try:
|
|
101
|
+
r = requests.get(f"http://127.0.0.1:{self.port}/json")
|
|
102
|
+
targets = json.loads(r.text)
|
|
103
|
+
if len(targets) == 0:
|
|
104
|
+
time.sleep(1)
|
|
105
|
+
continue
|
|
106
|
+
break
|
|
107
|
+
except requests.exceptions.ConnectionError:
|
|
108
|
+
time.sleep(1)
|
|
109
|
+
|
|
110
|
+
if r is None:
|
|
111
|
+
raise RuntimeError("Cannot connect to chrome debugging port")
|
|
112
|
+
|
|
113
|
+
if len(targets) == 0:
|
|
114
|
+
raise RuntimeError("Cannot create websocket connection with debugger")
|
|
115
|
+
|
|
116
|
+
self.ws = websocket.create_connection( # type: ignore
|
|
117
|
+
targets[target_id]["webSocketDebuggerUrl"]
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
def send_cmd(self, command: Dict[Any, Any]) -> Union[str, bytes]:
|
|
121
|
+
if command.get("id") is None:
|
|
122
|
+
command["id"] = self.cmd_id
|
|
123
|
+
for _ in range(3):
|
|
124
|
+
try:
|
|
125
|
+
self.ws.send(json.dumps(command))
|
|
126
|
+
r = self.ws.recv()
|
|
127
|
+
self.cmd_id += 1
|
|
128
|
+
return r
|
|
129
|
+
except BrokenPipeError:
|
|
130
|
+
self.connect()
|
|
131
|
+
|
|
132
|
+
raise RuntimeError("Websocket keep disconnecting")
|
|
133
|
+
|
|
134
|
+
def exec_js(self, js: str, context_id: Optional[int] = None) -> Union[str, bytes]:
|
|
135
|
+
command: Dict[str, Any] = {
|
|
136
|
+
"id": self.cmd_id,
|
|
137
|
+
"method": "Runtime.evaluate",
|
|
138
|
+
"params": {"expression": js},
|
|
139
|
+
}
|
|
140
|
+
if context_id is not None:
|
|
141
|
+
command["params"]["contextId"] = context_id
|
|
142
|
+
return self.send_cmd(command)
|
|
143
|
+
|
|
144
|
+
def set_transparent_bg(self) -> Union[str, bytes]:
|
|
145
|
+
command: Dict[str, Any] = {
|
|
146
|
+
"id": self.cmd_id,
|
|
147
|
+
"method": "Emulation.setDefaultBackgroundColorOverride",
|
|
148
|
+
"params": {"color": {"r": 0, "g": 0, "b": 0, "a": 0}},
|
|
149
|
+
}
|
|
150
|
+
return self.send_cmd(command)
|
|
151
|
+
|
|
152
|
+
def screenshot(self, clip: Optional[Dict[str, int]] = None) -> Image.Image:
|
|
153
|
+
command: Dict[str, Any] = {
|
|
154
|
+
"id": self.cmd_id,
|
|
155
|
+
"method": "Page.captureScreenshot",
|
|
156
|
+
"params": {"captureBeyondViewport": True, "optimizeForSpeed": True},
|
|
157
|
+
}
|
|
158
|
+
if clip:
|
|
159
|
+
command["params"]["clip"] = clip
|
|
160
|
+
result = self.send_cmd(command)
|
|
161
|
+
return Image.open(
|
|
162
|
+
io.BytesIO(base64.b64decode(json.loads(result)["result"]["data"]))
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
def get_curr_url(self) -> str:
|
|
166
|
+
r = self.exec_js("window.location.href")
|
|
167
|
+
return cast(
|
|
168
|
+
str, json.loads(r).get("result", {}).get("result", {}).get("value", "")
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
def navigate(self, url: str) -> None:
|
|
172
|
+
command = {"id": self.cmd_id, "method": "Page.navigate", "params": {"url": url}}
|
|
173
|
+
self.send_cmd(command)
|
|
174
|
+
|
|
175
|
+
def open_html_str(self, html: str) -> None:
|
|
176
|
+
command: Dict[str, Any] = {
|
|
177
|
+
"id": self.cmd_id,
|
|
178
|
+
"method": "Page.navigate",
|
|
179
|
+
"params": {"url": "about:blank"},
|
|
180
|
+
}
|
|
181
|
+
result = cast(str, self.send_cmd(command))
|
|
182
|
+
frame_id = json.loads(result).get("result", {}).get("frameId", None)
|
|
183
|
+
if frame_id is None:
|
|
184
|
+
raise RuntimeError(f"Cannot navigate to about:blank ({result})")
|
|
185
|
+
|
|
186
|
+
self.exec_js('document.getElementsByTagName("html")[0].remove()')
|
|
187
|
+
|
|
188
|
+
command = {
|
|
189
|
+
"id": self.cmd_id,
|
|
190
|
+
"method": "Page.setDocumentContent",
|
|
191
|
+
"params": {"frameId": frame_id, "html": html},
|
|
192
|
+
}
|
|
193
|
+
self.send_cmd(command)
|
|
194
|
+
|
|
195
|
+
def runtime_enable(self) -> None:
|
|
196
|
+
command = {
|
|
197
|
+
"method": "Runtime.enable",
|
|
198
|
+
}
|
|
199
|
+
self.send_cmd(command)
|
|
200
|
+
|
|
201
|
+
def runtime_disable(self) -> None:
|
|
202
|
+
command = {
|
|
203
|
+
"method": "Runtime.disable",
|
|
204
|
+
}
|
|
205
|
+
self.send_cmd(command)
|
|
206
|
+
|
|
207
|
+
def reload(self) -> None:
|
|
208
|
+
command = {
|
|
209
|
+
"method": "Page.reload",
|
|
210
|
+
}
|
|
211
|
+
self.send_cmd(command)
|
|
212
|
+
|
|
213
|
+
def close(self) -> None:
|
|
214
|
+
command = {
|
|
215
|
+
"method": "Browser.close",
|
|
216
|
+
}
|
|
217
|
+
self.send_cmd(command)
|
|
218
|
+
self.ws.close()
|
|
219
|
+
os.kill(self.chrome_proc.pid, signal.SIGTERM)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import configparser
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
# Adopted from https://github.com/roniemartinez/browsers/blob/master/browsers/linux.py
|
|
8
|
+
|
|
9
|
+
LINUX_DESKTOP_ENTRY_LIST = (
|
|
10
|
+
("chrome", ("google-chrome",)),
|
|
11
|
+
("chromium", ("chromium", "chromium_chromium")),
|
|
12
|
+
("brave", ("brave-browser", "brave_brave")),
|
|
13
|
+
("brave-beta", ("brave-browser-beta",)),
|
|
14
|
+
("brave-nightly", ("brave-browser-nightly",)),
|
|
15
|
+
("msedge", ("microsoft-edge",)),
|
|
16
|
+
("opera", ("opera_opera",)),
|
|
17
|
+
("opera-beta", ("opera-beta_opera-beta",)),
|
|
18
|
+
("opera-developer", ("opera-developer_opera-developer",)),
|
|
19
|
+
("vivaldi", ("vivaldi_vivaldi-stable",)),
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
# $XDG_DATA_HOME and $XDG_DATA_DIRS are not always set
|
|
23
|
+
XDG_DATA_LOCATIONS = (
|
|
24
|
+
"~/.local/share/applications",
|
|
25
|
+
"/usr/share/applications",
|
|
26
|
+
"/var/lib/snapd/desktop/applications",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
VERSION_PATTERN = re.compile(
|
|
30
|
+
r"\b(\S+\.\S+)\b"
|
|
31
|
+
) # simple pattern assuming all version strings have a dot on them
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_chromium_path() -> Optional[str]:
|
|
35
|
+
for _, desktop_entries in LINUX_DESKTOP_ENTRY_LIST:
|
|
36
|
+
for application_dir in XDG_DATA_LOCATIONS:
|
|
37
|
+
for desktop_entry in desktop_entries:
|
|
38
|
+
path = os.path.join(application_dir, f"{desktop_entry}.desktop")
|
|
39
|
+
|
|
40
|
+
if not os.path.isfile(path):
|
|
41
|
+
continue
|
|
42
|
+
|
|
43
|
+
config = configparser.ConfigParser(interpolation=None)
|
|
44
|
+
config.read(path, encoding="utf-8")
|
|
45
|
+
executable_path = config.get("Desktop Entry", "Exec")
|
|
46
|
+
|
|
47
|
+
if executable_path.lower().endswith(" %u"):
|
|
48
|
+
executable_path = executable_path[:-3].strip()
|
|
49
|
+
|
|
50
|
+
return executable_path
|
|
51
|
+
|
|
52
|
+
return None
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import glob
|
|
3
|
+
import os
|
|
4
|
+
import plistlib
|
|
5
|
+
import subprocess
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
# Adopted from https://github.com/roniemartinez/browsers/blob/master/browsers/osx.py
|
|
9
|
+
|
|
10
|
+
OSX_CHROMIUMS_BUNDLE_LIST = (
|
|
11
|
+
# browser name, bundle ID, version string
|
|
12
|
+
("chrome", "com.google.Chrome", "CFBundleShortVersionString"),
|
|
13
|
+
("chrome-beta", "com.google.Chrome.beta", "CFBundleShortVersionString"),
|
|
14
|
+
("chrome-canary", "com.google.Chrome.canary", "CFBundleShortVersionString"),
|
|
15
|
+
("chrome-dev", "com.google.Chrome.dev", "CFBundleShortVersionString"),
|
|
16
|
+
("chrome-test", "com.google.chrome.for.testing", "CFBundleShortVersionString"),
|
|
17
|
+
("chromium", "org.chromium.Chromium", "CFBundleShortVersionString"),
|
|
18
|
+
("msedge", "com.microsoft.edgemac", "CFBundleShortVersionString"),
|
|
19
|
+
("msedge-beta", "com.microsoft.edgemac.Beta", "CFBundleShortVersionString"),
|
|
20
|
+
("msedge-dev", "com.microsoft.edgemac.Dev", "CFBundleShortVersionString"),
|
|
21
|
+
("msedge-canary", "com.microsoft.edgemac.Canary", "CFBundleShortVersionString"),
|
|
22
|
+
("brave", "com.brave.Browser", "CFBundleVersion"),
|
|
23
|
+
("brave-beta", "com.brave.Browser.beta", "CFBundleVersion"),
|
|
24
|
+
("brave-dev", "com.brave.Browser.dev", "CFBundleVersion"),
|
|
25
|
+
("brave-nightly", "com.brave.Browser.nightly", "CFBundleVersion"),
|
|
26
|
+
("opera", "com.operasoftware.Opera", "CFBundleVersion"),
|
|
27
|
+
("opera-beta", "com.operasoftware.OperaNext", "CFBundleVersion"),
|
|
28
|
+
("opera-developer", "com.operasoftware.OperaDeveloper", "CFBundleVersion"),
|
|
29
|
+
("opera-gx", "com.operasoftware.OperaGX", "CFBundleVersion"),
|
|
30
|
+
("opera-neon", "com.opera.Neon", "CFBundleShortVersionString"),
|
|
31
|
+
("duckduckgo", "com.duckduckgo.macos.browser", "CFBundleShortVersionString"),
|
|
32
|
+
("epic", "com.hiddenreflex.Epic", "CFBundleShortVersionString"),
|
|
33
|
+
("vivaldi", "com.vivaldi.Vivaldi", "CFBundleShortVersionString"),
|
|
34
|
+
("yandex", "ru.yandex.desktop.yandex-browser", "CFBundleShortVersionString"),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
OSX_CHROMIUMS_BUNDLE_DICT = {item[1]: item for item in OSX_CHROMIUMS_BUNDLE_LIST}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_chromium_path() -> Optional[str]:
|
|
41
|
+
for _, bundle_id, _ in OSX_CHROMIUMS_BUNDLE_LIST:
|
|
42
|
+
app_dirs = subprocess.getoutput(
|
|
43
|
+
f'mdfind "kMDItemCFBundleIdentifier == {bundle_id}"'
|
|
44
|
+
).splitlines()
|
|
45
|
+
for app_dir in app_dirs:
|
|
46
|
+
plist_path = os.path.join(app_dir, "Contents/Info.plist")
|
|
47
|
+
|
|
48
|
+
with open(plist_path, "rb") as f:
|
|
49
|
+
plist = plistlib.load(f)
|
|
50
|
+
executable_name = plist["CFBundleExecutable"]
|
|
51
|
+
executable = os.path.join(app_dir, "Contents/MacOS", executable_name)
|
|
52
|
+
return executable
|
|
53
|
+
|
|
54
|
+
# Naively iterate /Applications folder in case the above check fails
|
|
55
|
+
for plist_path in glob.glob("/Applications/*.app/Contents/Info.plist"):
|
|
56
|
+
with open(plist_path, "rb") as f:
|
|
57
|
+
plist = plistlib.load(f)
|
|
58
|
+
bundle_id = plist.get("CFBundleIdentifier")
|
|
59
|
+
if bundle_id not in OSX_CHROMIUMS_BUNDLE_DICT:
|
|
60
|
+
continue
|
|
61
|
+
|
|
62
|
+
app_dir = os.path.dirname(os.path.dirname(plist_path))
|
|
63
|
+
executable_name = plist["CFBundleExecutable"]
|
|
64
|
+
executable = os.path.join(app_dir, "Contents/MacOS", executable_name)
|
|
65
|
+
|
|
66
|
+
return executable
|
|
67
|
+
|
|
68
|
+
return None
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import os
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
# Not adopting approach of reading registry like https://github.com/roniemartinez/browsers
|
|
6
|
+
# As trigger antivirus
|
|
7
|
+
|
|
8
|
+
PF32 = os.environ.get("PROGRAMW6432")
|
|
9
|
+
PF64 = os.environ.get("PROGRAMFILES")
|
|
10
|
+
LAD = os.environ.get("LOCALAPPDATA")
|
|
11
|
+
|
|
12
|
+
WINDOWS_CHROMIUMS_PATHS = (
|
|
13
|
+
f"{PF64}\\Google\\Chrome\\Application\\chrome.exe", # chromium
|
|
14
|
+
f"{PF32}\\Google\\Chrome\\Application\\chrome.exe", # chromium
|
|
15
|
+
f"{LAD}\\Google\\Chrome SxS\\Application\\chrome.exe", # chromium-canary
|
|
16
|
+
f"{PF64}\\Opera\\opera.exe", # opera
|
|
17
|
+
f"{PF32}\\Opera\\opera.exe", # opera
|
|
18
|
+
f"{LAD}\\Programs\\Opera\\opera.exe", # opera
|
|
19
|
+
f"{LAD}\\Programs\\Opera\\launcher.exe", # opera
|
|
20
|
+
f"{LAD}\\Programs\\Opera Beta\\opera.exe", # opera-beta
|
|
21
|
+
f"{LAD}\\Programs\\Opera Beta\\launcher.exe", # opera-beta
|
|
22
|
+
f"{LAD}\\Programs\\Opera Developer\\opera.exe", # opera-developer
|
|
23
|
+
f"{LAD}\\Programs\\Opera Developer\\launcher.exe", # opera-developer
|
|
24
|
+
f"{PF64}\\Microsoft\\Edge\\Application\\msedge.exe", # msedge
|
|
25
|
+
f"{PF32}\\Microsoft\\Edge\\Application\\msedge.exe", # msedge
|
|
26
|
+
f"{PF64}\\Microsoft\\Edge Beta\\Application\\msedge.exe", # msedge-beta
|
|
27
|
+
f"{PF32}\\Microsoft\\Edge Beta\\Application\\msedge.exe", # msedge-beta
|
|
28
|
+
f"{PF64}\\Microsoft\\Edge Dev\\Application\\msedge.exe", # msedge-dev
|
|
29
|
+
f"{PF32}\\Microsoft\\Edge Dev\\Application\\msedge.exe", # msedge-dev
|
|
30
|
+
f"{LAD}\\Microsoft\\Edge SxS\\Application\\msedge.exe", # msedge-canary
|
|
31
|
+
f"{PF64}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe", # brave
|
|
32
|
+
f"{PF32}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe", # brave
|
|
33
|
+
f"{PF64}\\BraveSoftware\\Brave-Browser-Beta\\Application\\brave.exe", # brave-beta
|
|
34
|
+
f"{PF32}\\BraveSoftware\\Brave-Browser-Beta\\Application\\brave.exe", # brave-beta
|
|
35
|
+
f"{PF64}\\BraveSoftware\\Brave-Browser-Nightly\\Application\\brave.exe", # brave-nightly
|
|
36
|
+
f"{PF32}\\BraveSoftware\\Brave-Browser-Nightly\\Application\\brave.exe", # brave-nightly
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_chromium_path() -> Optional[str]:
|
|
41
|
+
for path in WINDOWS_CHROMIUMS_PATHS:
|
|
42
|
+
if os.path.exists(path):
|
|
43
|
+
return path
|
|
44
|
+
|
|
45
|
+
return None
|