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.
Files changed (32) hide show
  1. sticker_convert/cli.py +3 -0
  2. sticker_convert/converter.py +61 -54
  3. sticker_convert/downloaders/download_band.py +110 -0
  4. sticker_convert/downloaders/download_kakao.py +84 -22
  5. sticker_convert/gui.py +14 -3
  6. sticker_convert/gui_components/windows/advanced_compression_window.py +17 -0
  7. sticker_convert/gui_components/windows/discord_get_auth_window.py +3 -3
  8. sticker_convert/gui_components/windows/kakao_get_auth_window.py +2 -6
  9. sticker_convert/gui_components/windows/signal_get_auth_window.py +3 -3
  10. sticker_convert/gui_components/windows/viber_get_auth_window.py +1 -1
  11. sticker_convert/job.py +6 -0
  12. sticker_convert/job_option.py +2 -0
  13. sticker_convert/resources/compression.json +47 -0
  14. sticker_convert/resources/help.json +1 -0
  15. sticker_convert/resources/input.json +10 -0
  16. sticker_convert/resources/memdump_linux.sh +0 -1
  17. sticker_convert/utils/auth/get_discord_auth.py +1 -1
  18. sticker_convert/utils/auth/get_kakao_desktop_auth.py +119 -35
  19. sticker_convert/utils/auth/get_signal_auth.py +2 -2
  20. sticker_convert/utils/auth/get_viber_auth.py +1 -1
  21. sticker_convert/utils/auth/telethon_setup.py +3 -1
  22. sticker_convert/utils/chrome_remotedebug.py +25 -13
  23. sticker_convert/utils/media/codec_info.py +1 -1
  24. sticker_convert/utils/singletons.py +18 -0
  25. sticker_convert/utils/url_detect.py +3 -0
  26. sticker_convert/version.py +1 -1
  27. {sticker_convert-2.12.3.dist-info → sticker_convert-2.13.0.dist-info}/METADATA +35 -27
  28. {sticker_convert-2.12.3.dist-info → sticker_convert-2.13.0.dist-info}/RECORD +32 -30
  29. {sticker_convert-2.12.3.dist-info → sticker_convert-2.13.0.dist-info}/WHEEL +1 -1
  30. {sticker_convert-2.12.3.dist-info → sticker_convert-2.13.0.dist-info}/entry_points.txt +0 -0
  31. {sticker_convert-2.12.3.dist-info → sticker_convert-2.13.0.dist-info}/licenses/LICENSE +0 -0
  32. {sticker_convert-2.12.3.dist-info → sticker_convert-2.13.0.dist-info}/top_level.txt +0 -0
sticker_convert/cli.py CHANGED
@@ -139,6 +139,7 @@ class CLI:
139
139
  "cache_dir",
140
140
  "scale_filter",
141
141
  "quantize_method",
142
+ "chromium_path",
142
143
  )
143
144
  flags_comp_bool = ("fake_vid",)
144
145
  keyword_args: Dict[str, Any]
@@ -231,6 +232,7 @@ class CLI:
231
232
  "telegram": args.download_telegram,
232
233
  "telegram_telethon": args.download_telegram_telethon,
233
234
  "kakao": args.download_kakao,
235
+ "band": args.download_band,
234
236
  "viber": args.download_viber,
235
237
  "discord": args.download_discord,
236
238
  "discord_emoji": args.download_discord_emoji,
@@ -427,6 +429,7 @@ class CLI:
427
429
  fake_vid=self.compression_presets[preset]["fake_vid"]
428
430
  if args.fake_vid is None
429
431
  else args.fake_vid,
432
+ chromium_path=args.chromium_path,
430
433
  cache_dir=args.cache_dir,
431
434
  scale_filter=self.compression_presets[preset]["scale_filter"]
432
435
  if args.scale_filter is None
@@ -19,6 +19,7 @@ from sticker_convert.utils.chrome_remotedebug import CRD
19
19
  from sticker_convert.utils.files.cache_store import CacheStore
20
20
  from sticker_convert.utils.media.codec_info import CodecInfo, rounding
21
21
  from sticker_convert.utils.media.format_verify import FormatVerify
22
+ from sticker_convert.utils.singletons import singletons
22
23
 
23
24
  if TYPE_CHECKING:
24
25
  from av.video.frame import VideoFrame
@@ -435,22 +436,30 @@ class StickerConvert:
435
436
  width = self.codec_info_orig.res[0]
436
437
  height = self.codec_info_orig.res[1]
437
438
 
438
- chrome_path = CRD.get_chrome_path()
439
- args = [
440
- "--headless",
441
- "--kiosk",
442
- "--disable-extensions",
443
- "--disable-infobars",
444
- "--disable-gpu",
445
- "--disable-gpu-rasterization",
446
- "--hide-scrollbars",
447
- f"--window-size={width + 200},{height + 200}",
448
- "about:blank",
449
- ]
450
- if chrome_path is None:
451
- raise RuntimeError("[F] Chrome/Chromium required for importing svg")
452
- self.cb.put("[W] Importing SVG takes long time")
453
-
439
+ if singletons.objs.get("crd") is None:
440
+ chrome_path: Optional[str]
441
+ if self.opt_comp.chromium_path:
442
+ chrome_path = self.opt_comp.chromium_path
443
+ else:
444
+ chrome_path = CRD.get_chrome_path()
445
+ args = [
446
+ "--headless",
447
+ "--kiosk",
448
+ "--disable-extensions",
449
+ "--disable-infobars",
450
+ "--disable-gpu",
451
+ "--disable-gpu-rasterization",
452
+ "--hide-scrollbars",
453
+ "--force-device-scale-factor=1",
454
+ "about:blank",
455
+ ]
456
+ if chrome_path is None:
457
+ raise RuntimeError("[F] Chrome/Chromium required for importing svg")
458
+ self.cb.put("[W] Importing SVG takes long time")
459
+ singletons.objs["crd"] = CRD(chrome_path, args=args)
460
+ singletons.objs["crd"].connect(-1) # type: ignore
461
+
462
+ crd = cast(CRD, singletons.objs["crd"])
454
463
  if isinstance(self.in_f, bytes):
455
464
  svg = self.in_f.decode()
456
465
  else:
@@ -465,39 +474,35 @@ class StickerConvert:
465
474
  svg_tag["height"] = height
466
475
  svg = str(soup)
467
476
 
468
- crd = None
469
- try:
470
- crd = CRD(chrome_path, args=args)
471
- crd.connect(-1)
472
- crd.open_html_str(svg)
473
- crd.set_transparent_bg()
474
- crd.exec_js('svg = document.getElementsByTagName("svg")[0]')
475
- x = json.loads(crd.exec_js("svg.getBoundingClientRect().x"))["result"][
476
- "result"
477
- ]["value"]
478
- y = json.loads(crd.exec_js("svg.getBoundingClientRect().y"))["result"][
479
- "result"
480
- ]["value"]
481
- clip = {"x": x, "y": y, "width": width, "height": height, "scale": 1}
482
-
483
- if self.codec_info_orig.fps > 0:
484
- crd.exec_js("svg.pauseAnimations()")
485
- for i in range(self.codec_info_orig.frames):
486
- curr_time = (
487
- i
488
- / self.codec_info_orig.frames
489
- * self.codec_info_orig.duration
490
- / 1000
491
- )
492
- crd.exec_js(f"svg.setCurrentTime({curr_time})")
493
- self.frames_raw.append(
494
- np.asarray(crd.screenshot(clip).convert("RGBA"))
495
- )
496
- else:
497
- self.frames_raw.append(np.asarray(crd.screenshot(clip).convert("RGBA")))
498
- finally:
499
- if crd is not None:
500
- crd.close()
477
+ crd.open_html_str(svg)
478
+ crd.set_transparent_bg()
479
+ init_js = 'svg = document.getElementsByTagName("svg")[0];'
480
+ if self.codec_info_orig.fps > 0:
481
+ init_js += "svg.pauseAnimations();"
482
+ init_js += "JSON.stringify(svg.getBoundingClientRect());"
483
+ bound = json.loads(
484
+ json.loads(crd.exec_js(init_js))["result"]["result"]["value"]
485
+ )
486
+ clip = {
487
+ "x": bound["x"],
488
+ "y": bound["y"],
489
+ "width": width,
490
+ "height": height,
491
+ "scale": 1,
492
+ }
493
+
494
+ if self.codec_info_orig.fps > 0:
495
+ for i in range(self.codec_info_orig.frames):
496
+ curr_time = (
497
+ i
498
+ / self.codec_info_orig.frames
499
+ * self.codec_info_orig.duration
500
+ / 1000
501
+ )
502
+ crd.exec_js(f"svg.setCurrentTime({curr_time})")
503
+ self.frames_raw.append(np.asarray(crd.screenshot(clip)))
504
+ else:
505
+ self.frames_raw.append(np.asarray(crd.screenshot(clip)))
501
506
 
502
507
  def _frames_import_pillow(self) -> None:
503
508
  with Image.open(self.in_f) as im:
@@ -559,7 +564,7 @@ class StickerConvert:
559
564
  container = cast(InputContainer, container)
560
565
  context = container.streams.video[0].codec_context
561
566
  if context.name == "vp8":
562
- context = cast(VideoCodecContext, CodecContext.create("libvpx", "r"))
567
+ context = CodecContext.create("libvpx", "r")
563
568
  elif context.name == "vp9":
564
569
  context = cast(
565
570
  VideoCodecContext, CodecContext.create("libvpx-vp9", "r")
@@ -592,7 +597,12 @@ class StickerConvert:
592
597
  else:
593
598
  frame_resized = frame
594
599
 
595
- if frame_resized.format.name == "yuv420p":
600
+ # yuva420p may cause crash for pyav < 14
601
+ # Not safe to directly call frame.to_ndarray(format="rgba")
602
+ # https://github.com/PyAV-Org/PyAV/discussions/1510
603
+ if int(av.__version__.split(".")[0]) >= 14:
604
+ rgba_array = frame_resized.to_ndarray(format="rgba")
605
+ elif frame_resized.format.name == "yuv420p":
596
606
  rgb_array = frame_resized.to_ndarray(format="rgb24")
597
607
  rgba_array = np.dstack(
598
608
  (
@@ -604,9 +614,6 @@ class StickerConvert:
604
614
  )
605
615
  )
606
616
  else:
607
- # yuva420p may cause crash
608
- # Not safe to directly call frame.to_ndarray(format="rgba")
609
- # https://github.com/laggykiller/sticker-convert/issues/114
610
617
  frame_resized = frame_resized.reformat(
611
618
  format="yuva420p",
612
619
  dst_colorspace=1,
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ import zipfile
6
+ from io import BytesIO
7
+ from pathlib import Path
8
+ from typing import Any, Dict, Optional, Tuple
9
+ from urllib.parse import urlparse
10
+
11
+ import requests
12
+
13
+ from sticker_convert.downloaders.download_base import DownloadBase
14
+ from sticker_convert.job_option import CredOption, InputOption
15
+ from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
16
+ from sticker_convert.utils.files.metadata_handler import MetadataHandler
17
+
18
+
19
+ class DownloadBand(DownloadBase):
20
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
21
+ super().__init__(*args, **kwargs)
22
+
23
+ def decompress(self, zip_file: bytes) -> int:
24
+ with zipfile.ZipFile(BytesIO(zip_file)) as zf:
25
+ self.cb.put("Unzipping...")
26
+
27
+ zf_files = zf.namelist()
28
+ animated = [i for i in zf_files if "animation/" in i]
29
+ if len(animated) > 0:
30
+ pack_files = animated
31
+ else:
32
+ pack_files = [
33
+ i
34
+ for i in zf_files
35
+ if i.endswith(".meta") is False and "_key" not in i
36
+ ]
37
+
38
+ self.cb.put(
39
+ (
40
+ "bar",
41
+ None,
42
+ {"set_progress_mode": "determinate", "steps": len(pack_files)},
43
+ )
44
+ )
45
+
46
+ for num, sticker in enumerate(pack_files):
47
+ data = zf.read(sticker)
48
+ self.cb.put(f"Read {sticker}")
49
+ ext = sticker.split(".")[-1]
50
+
51
+ out_path = Path(self.out_dir, str(num).zfill(3) + f".{ext}")
52
+ with open(out_path, "wb") as f:
53
+ f.write(data)
54
+
55
+ self.cb.put("update_bar")
56
+
57
+ return len(pack_files)
58
+
59
+ def get_metadata(self, pack_no: str) -> Optional[Dict[Any, Any]]:
60
+ r = requests.get(
61
+ f"https://sapi.band.us/v1.0.0/get_sticker_info?pack_no={pack_no}"
62
+ )
63
+ if r.text:
64
+ return json.loads(r.text)
65
+ else:
66
+ return None
67
+
68
+ def download_stickers_band(self) -> Tuple[int, int]:
69
+ if urlparse(self.url).netloc != "www.band.us" and self.url.isnumeric() is False:
70
+ self.cb.put("Download failed: Unsupported URL format")
71
+ return 0, 0
72
+
73
+ if self.url.isnumeric():
74
+ pack_no = self.url
75
+ else:
76
+ pack_no = urlparse(self.url).path.split("/")[-1]
77
+ metadata = self.get_metadata(pack_no)
78
+ if metadata:
79
+ self.title = metadata["result_data"]["sticker"]["name"]
80
+ else:
81
+ self.cb.put("Download failed: Failed to get metadata")
82
+ return 0, 0
83
+
84
+ MetadataHandler.set_metadata(self.out_dir, title=self.title)
85
+
86
+ pack_url = f"http://s.cmstatic.net/band/sticker/v2/{pack_no}/shop/pack"
87
+ zip_file = self.download_file(pack_url)
88
+
89
+ if zip_file:
90
+ self.cb.put(f"Downloaded {pack_url}")
91
+ else:
92
+ self.cb.put(f"Cannot download {pack_url}")
93
+ return 0, 0
94
+
95
+ pack_files_no = self.decompress(zip_file)
96
+
97
+ cover_url = f"http://s.cmstatic.net/band/sticker/v2/{pack_no}/shop/main"
98
+ self.download_file(cover_url, self.out_dir / "cover.png")
99
+
100
+ return pack_files_no, pack_files_no
101
+
102
+ @staticmethod
103
+ def start(
104
+ opt_input: InputOption,
105
+ opt_cred: Optional[CredOption],
106
+ cb: CallbackProtocol,
107
+ cb_return: CallbackReturn,
108
+ ) -> Tuple[int, int]:
109
+ downloader = DownloadBand(opt_input, opt_cred, cb, cb_return)
110
+ return downloader.download_stickers_band()
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
  import itertools
5
5
  import json
6
6
  from pathlib import Path
7
- from typing import Any, List, Optional, Tuple
7
+ from typing import Any, Dict, List, Optional, Tuple
8
8
  from urllib.parse import urlparse
9
9
 
10
10
  import requests
@@ -17,6 +17,13 @@ from sticker_convert.utils.media.decrypt_kakao import DecryptKakao
17
17
 
18
18
 
19
19
  class MetadataKakao:
20
+ @staticmethod
21
+ def share_link_to_public_link(share_link: str) -> str:
22
+ # Share link redirect to preview link if use desktop headers
23
+ headers_desktop = {"User-Agent": "Chrome"}
24
+ r = requests.get(share_link, headers=headers_desktop, allow_redirects=True)
25
+ return r.url
26
+
20
27
  @staticmethod
21
28
  def get_item_code_from_hash(hash: str, auth_token: str) -> Optional[str]:
22
29
  headers = {
@@ -40,12 +47,14 @@ class MetadataKakao:
40
47
  return item_code
41
48
 
42
49
  @staticmethod
43
- def get_item_code_from_title(title_ko: str, auth_token: str) -> Optional[str]:
50
+ def get_item_code_from_search(
51
+ pack_title: str, search_term: str, by_author: bool, auth_token: str
52
+ ) -> str:
44
53
  headers = {
45
54
  "Authorization": auth_token,
46
55
  }
47
56
 
48
- data = {"query": title_ko}
57
+ data = {"query": search_term}
49
58
 
50
59
  response = requests.post(
51
60
  "https://talk-pilsner.kakao.com/emoticon/item_store/instant_search",
@@ -54,12 +63,53 @@ class MetadataKakao:
54
63
  )
55
64
 
56
65
  if response.status_code != 200:
57
- return None
66
+ return "auth_error"
67
+
68
+ def check_pack_match(pack_info: Dict[str, Any]) -> bool:
69
+ share_link = pack_info["itemMetaInfo"]["shareData"]["linkUrl"]
70
+ public_url = MetadataKakao.share_link_to_public_link(share_link)
71
+ if pack_title == urlparse(public_url).path.split("/")[-1]:
72
+ return True
73
+ return False
58
74
 
59
75
  response_json = json.loads(response.text)
60
- item_code = response_json["emoticons"][0]["item_code"]
76
+ for emoticon in response_json["emoticons"]:
77
+ item_code = emoticon["item_code"]
78
+ pack_info = MetadataKakao.get_pack_info_authed(item_code, auth_token)
79
+ if pack_info is None:
80
+ continue
81
+ if check_pack_match(pack_info):
82
+ return item_code
83
+ if by_author:
84
+ cid = pack_info["itemMetaInfo"]["itemMetaData"]["cid"]
85
+ for item_code in MetadataKakao.get_items_by_creator(cid, auth_token):
86
+ pack_info = MetadataKakao.get_pack_info_authed(
87
+ item_code, auth_token
88
+ )
89
+ if pack_info is None:
90
+ continue
91
+ if check_pack_match(pack_info):
92
+ return item_code
61
93
 
62
- return item_code
94
+ return "code_not_found"
95
+
96
+ @staticmethod
97
+ def get_items_by_creator(cid: str, auth_token: str) -> List[str]:
98
+ headers = {"Authorization": auth_token}
99
+
100
+ params = {
101
+ "itemSort": "NEW",
102
+ "offset": "0",
103
+ "size": "30",
104
+ }
105
+
106
+ response = requests.get(
107
+ f"https://talk-pilsner.kakao.com/emoticon/api/store/v3/creators/{cid}",
108
+ headers=headers,
109
+ params=params,
110
+ )
111
+
112
+ return [i["item_id"] for i in json.loads(response.text)["items"]]
63
113
 
64
114
  @staticmethod
65
115
  def get_pack_info_unauthed(
@@ -119,13 +169,8 @@ class DownloadKakao(DownloadBase):
119
169
  hash = urlparse(self.url).path.split("/")[-1]
120
170
  item_code = MetadataKakao.get_item_code_from_hash(hash, self.auth_token)
121
171
 
122
- # Share link redirect to preview link if use desktop headers
123
- # This allows us to find pack author
124
- headers_desktop = {"User-Agent": "Chrome"}
125
-
126
- r = requests.get(self.url, headers=headers_desktop, allow_redirects=True)
127
-
128
- self.pack_title = urlparse(r.url).path.split("/")[-1]
172
+ public_url = MetadataKakao.share_link_to_public_link(self.url)
173
+ self.pack_title = urlparse(public_url).path.split("/")[-1]
129
174
  pack_info_unauthed = MetadataKakao.get_pack_info_unauthed(self.pack_title)
130
175
  if pack_info_unauthed is None:
131
176
  self.cb.put(
@@ -184,14 +229,33 @@ class DownloadKakao(DownloadBase):
184
229
  thumbnail_urls = self.pack_info_unauthed["result"]["thumbnailUrls"]
185
230
 
186
231
  if self.auth_token:
187
- item_code = MetadataKakao.get_item_code_from_title(
188
- title_ko, self.auth_token
232
+ item_code = MetadataKakao.get_item_code_from_search(
233
+ self.pack_title, title_ko, False, self.auth_token
189
234
  )
190
- if item_code:
235
+ if item_code == "auth_error":
236
+ msg = "Warning: Cannot get item code.\n"
237
+ msg += "Is auth_token invalid / expired? Try to regenerate it.\n"
238
+ msg += "Continue to download static stickers instead?"
239
+ elif item_code == "code_not_found":
240
+ self.cb.put(
241
+ "Cannot get item code, trying to search by author name, this may take long time..."
242
+ )
243
+ self.cb.put(
244
+ "Hint: Use share link instead to download more reliably"
245
+ )
246
+ if self.author is not None:
247
+ item_code = MetadataKakao.get_item_code_from_search(
248
+ self.pack_title, self.author, True, self.auth_token
249
+ )
250
+ if item_code == "code_not_found":
251
+ msg = "Warning: Cannot get item code.\n"
252
+ msg += "Please use share link instead.\n"
253
+ msg += "Continue to download static stickers instead?"
254
+ else:
255
+ return self.download_animated(item_code)
256
+ else:
191
257
  return self.download_animated(item_code)
192
- msg = "Warning: Cannot get item code.\n"
193
- msg += "Is auth_token invalid / expired? Try to regenerate it.\n"
194
- msg += "Continue to download static stickers instead?"
258
+
195
259
  self.cb.put(("ask_bool", (msg,), None))
196
260
  if self.cb_return:
197
261
  response = self.cb_return.get_response()
@@ -249,9 +313,7 @@ class DownloadKakao(DownloadBase):
249
313
  if not self.pack_info_unauthed:
250
314
  public_url = None
251
315
  if urlparse(self.url).netloc == "emoticon.kakao.com":
252
- r = requests.get(self.url)
253
- # Share url would redirect to public url without headers
254
- public_url = r.url
316
+ public_url = MetadataKakao.share_link_to_public_link(self.url)
255
317
  elif urlparse(self.url).netloc == "e.kakao.com":
256
318
  public_url = self.url
257
319
  if public_url:
sticker_convert/gui.py CHANGED
@@ -137,6 +137,7 @@ class GUI(Window):
137
137
  self.fake_vid_var = BooleanVar()
138
138
  self.scale_filter_var = StringVar(self)
139
139
  self.quantize_method_var = StringVar(self)
140
+ self.chromium_path_var = StringVar(self)
140
141
  self.cache_dir_var = StringVar(self)
141
142
  self.default_emoji_var = StringVar(self)
142
143
  self.steps_var = IntVar(self)
@@ -321,6 +322,7 @@ class GUI(Window):
321
322
  "comp": {
322
323
  "no_compress": self.no_compress_var.get(),
323
324
  "preset": self.comp_preset_var.get(),
325
+ "chromium_path": self.chromium_path_var.get(),
324
326
  "cache_dir": self.cache_dir_var.get(),
325
327
  "processes": self.processes_var.get(),
326
328
  },
@@ -377,6 +379,9 @@ class GUI(Window):
377
379
  self.compression_presets["custom"], comp_custom
378
380
  )
379
381
  self.cache_dir_var.set(self.settings.get("comp", {}).get("cache_dir", ""))
382
+ self.chromium_path_var.set(
383
+ self.settings.get("comp", {}).get("chromium_path", "")
384
+ )
380
385
  self.processes_var.set(
381
386
  self.settings.get("comp", {}).get("processes", ceil(cpu_count() / 2))
382
387
  )
@@ -568,6 +573,9 @@ class GUI(Window):
568
573
  fake_vid=self.fake_vid_var.get(),
569
574
  scale_filter=self.scale_filter_var.get(),
570
575
  quantize_method=self.quantize_method_var.get(),
576
+ chromium_path=self.chromium_path_var.get()
577
+ if self.chromium_path_var.get() != ""
578
+ else None,
571
579
  cache_dir=self.cache_dir_var.get()
572
580
  if self.cache_dir_var.get() != ""
573
581
  else None,
@@ -706,9 +714,9 @@ class GUI(Window):
706
714
  set_progress_mode, steps, update_bar, *args, **kwargs
707
715
  )
708
716
 
709
- def highlight_fields(self):
717
+ def highlight_fields(self) -> bool:
710
718
  if not self.init_done:
711
- return
719
+ return True
712
720
 
713
721
  input_option = self.get_input_name()
714
722
  input_option_display = self.get_input_display_name()
@@ -754,7 +762,8 @@ class GUI(Window):
754
762
  download_option is None
755
763
  or input_option.startswith(download_option) is False
756
764
  and not (
757
- input_option in ("kakao", "line", "discord", "discord_emoji")
765
+ input_option
766
+ in ("kakao", "band", "line", "discord", "discord_emoji")
758
767
  and url.isnumeric()
759
768
  )
760
769
  ):
@@ -858,3 +867,5 @@ class GUI(Window):
858
867
  else:
859
868
  self.comp_frame.comp_preset_opt.config(bootstyle="secondary") # type: ignore
860
869
  self.output_frame.output_option_opt.config(bootstyle="secondary") # type: ignore
870
+
871
+ return True
@@ -293,6 +293,17 @@ class AdvancedCompressionWindow(BaseWindow):
293
293
  bootstyle="secondary", # type: ignore
294
294
  )
295
295
 
296
+ self.chromium_path_help_btn = self.add_help_btn(
297
+ self.gui.help["comp"]["chromium_path"]
298
+ )
299
+ self.chromium_path_lbl = Label(
300
+ self.frame_advcomp, text="Chromium/Chrome directory"
301
+ )
302
+ self.chromium_path_entry = Entry(
303
+ self.frame_advcomp, textvariable=self.gui.chromium_path_var, width=30
304
+ )
305
+ self.chromium_path_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
306
+
296
307
  self.cache_dir_help_btn = self.add_help_btn(self.gui.help["comp"]["cache_dir"])
297
308
  self.cache_dir_lbl = Label(self.frame_advcomp, text="Custom cache directory")
298
309
  self.cache_dir_entry = Entry(
@@ -412,6 +423,12 @@ class AdvancedCompressionWindow(BaseWindow):
412
423
  column=2, row=r, columnspan=4, sticky="nes", padx=3, pady=3
413
424
  )
414
425
  r += 1
426
+ self.chromium_path_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
427
+ self.chromium_path_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
428
+ self.chromium_path_entry.grid(
429
+ column=2, row=r, columnspan=4, sticky="nes", padx=3, pady=3
430
+ )
431
+ r += 1
415
432
  self.cache_dir_help_btn.grid(column=0, row=r, sticky="w", padx=3, pady=3)
416
433
  self.cache_dir_lbl.grid(column=1, row=r, sticky="w", padx=3, pady=3)
417
434
  self.cache_dir_entry.grid(
@@ -11,7 +11,7 @@ from sticker_convert.utils.auth.get_discord_auth import GetDiscordAuth
11
11
 
12
12
 
13
13
  class DiscordGetAuthWindow(BaseWindow):
14
- def __init__(self, *args: Any, **kwargs: Any):
14
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
15
15
  super(DiscordGetAuthWindow, self).__init__(*args, **kwargs)
16
16
 
17
17
  self.title("Get Discord token")
@@ -64,10 +64,10 @@ class DiscordGetAuthWindow(BaseWindow):
64
64
 
65
65
  GUIUtils.finalize_window(self)
66
66
 
67
- def cb_login(self):
67
+ def cb_login(self) -> None:
68
68
  Thread(target=self.cb_login_thread, daemon=True).start()
69
69
 
70
- def cb_login_thread(self, *args: Any):
70
+ def cb_login_thread(self, *args: Any) -> None:
71
71
  m = GetDiscordAuth(cb_msg=self.gui.cb_msg)
72
72
  discord_token, msg = m.get_cred()
73
73
  if discord_token:
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env python3
2
- import platform
3
- import subprocess
4
2
  from functools import partial
5
3
  from threading import Thread
6
4
  from typing import Any, Optional
@@ -37,7 +35,7 @@ class KakaoGetAuthWindow(BaseWindow):
37
35
  # Method 1 frame
38
36
  self.explanation1_1_lbl = Label(
39
37
  self.frame_from_desktop,
40
- text="This will get Kakao auth_token from Kakao Desktop (Windows only)",
38
+ text="This will get Kakao auth_token from Kakao Desktop",
41
39
  justify="left",
42
40
  anchor="w",
43
41
  )
@@ -70,14 +68,12 @@ class KakaoGetAuthWindow(BaseWindow):
70
68
  command=self.cb_launch_desktop,
71
69
  width=24,
72
70
  bootstyle="secondary", # type: ignore
73
- state="normal" if platform.system() == "Windows" else "disabled",
74
71
  )
75
72
  self.get_auth_desktop_btn = Button(
76
73
  self.frame_from_desktop,
77
74
  text="Get auth_token",
78
75
  command=self.cb_from_desktop,
79
76
  width=24,
80
- state="normal" if platform.system() == "Windows" else "disabled",
81
77
  )
82
78
 
83
79
  self.explanation1_1_lbl.grid(
@@ -270,7 +266,7 @@ class KakaoGetAuthWindow(BaseWindow):
270
266
  bin_path = m.get_kakao_desktop()
271
267
 
272
268
  if bin_path is not None:
273
- subprocess.Popen([bin_path])
269
+ m.launch_kakao(bin_path)
274
270
  else:
275
271
  self.cb_msg_block_kakao(
276
272
  "Error: Cannot launch Kakao Desktop. Is it installed?"
@@ -11,7 +11,7 @@ from sticker_convert.utils.auth.get_signal_auth import GetSignalAuth
11
11
 
12
12
 
13
13
  class SignalGetAuthWindow(BaseWindow):
14
- def __init__(self, *args: Any, **kwargs: Any):
14
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
15
15
  super(SignalGetAuthWindow, self).__init__(*args, **kwargs)
16
16
 
17
17
  self.title("Get Signal uuid and password")
@@ -64,10 +64,10 @@ class SignalGetAuthWindow(BaseWindow):
64
64
 
65
65
  GUIUtils.finalize_window(self)
66
66
 
67
- def cb_login(self):
67
+ def cb_login(self) -> None:
68
68
  Thread(target=self.cb_login_thread, daemon=True).start()
69
69
 
70
- def cb_login_thread(self, *args: Any):
70
+ def cb_login_thread(self, *args: Any) -> None:
71
71
  m = GetSignalAuth(cb_msg=self.gui.cb_msg, cb_ask_str=self.cb_ask_str_signal)
72
72
 
73
73
  uuid, password = m.get_cred()
@@ -109,7 +109,7 @@ class ViberGetAuthWindow(BaseWindow):
109
109
 
110
110
  GUIUtils.finalize_window(self)
111
111
 
112
- def cb_get_cred(self):
112
+ def cb_get_cred(self) -> None:
113
113
  Thread(target=self.cb_get_cred_thread, daemon=True).start()
114
114
 
115
115
  def cb_get_cred_thread(self) -> None: