sticker-convert 2.7.2__py3-none-any.whl → 2.7.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. sticker_convert/__init__.py +1 -0
  2. sticker_convert/__main__.py +3 -1
  3. sticker_convert/cli.py +20 -24
  4. sticker_convert/converter.py +108 -119
  5. sticker_convert/definitions.py +8 -12
  6. sticker_convert/downloaders/download_base.py +14 -31
  7. sticker_convert/downloaders/download_kakao.py +25 -39
  8. sticker_convert/downloaders/download_line.py +24 -33
  9. sticker_convert/downloaders/download_signal.py +7 -16
  10. sticker_convert/downloaders/download_telegram.py +6 -15
  11. sticker_convert/gui.py +53 -61
  12. sticker_convert/gui_components/frames/comp_frame.py +11 -20
  13. sticker_convert/gui_components/frames/config_frame.py +9 -9
  14. sticker_convert/gui_components/frames/control_frame.py +3 -3
  15. sticker_convert/gui_components/frames/cred_frame.py +12 -18
  16. sticker_convert/gui_components/frames/input_frame.py +9 -15
  17. sticker_convert/gui_components/frames/output_frame.py +9 -15
  18. sticker_convert/gui_components/frames/progress_frame.py +8 -8
  19. sticker_convert/gui_components/frames/right_clicker.py +2 -2
  20. sticker_convert/gui_components/gui_utils.py +6 -8
  21. sticker_convert/gui_components/windows/advanced_compression_window.py +23 -32
  22. sticker_convert/gui_components/windows/base_window.py +6 -6
  23. sticker_convert/gui_components/windows/kakao_get_auth_window.py +5 -11
  24. sticker_convert/gui_components/windows/line_get_auth_window.py +5 -5
  25. sticker_convert/gui_components/windows/signal_get_auth_window.py +6 -6
  26. sticker_convert/job.py +84 -90
  27. sticker_convert/job_option.py +36 -32
  28. sticker_convert/resources/emoji.json +334 -70
  29. sticker_convert/resources/help.json +1 -1
  30. sticker_convert/uploaders/compress_wastickers.py +19 -30
  31. sticker_convert/uploaders/upload_base.py +19 -13
  32. sticker_convert/uploaders/upload_signal.py +20 -33
  33. sticker_convert/uploaders/upload_telegram.py +21 -28
  34. sticker_convert/uploaders/xcode_imessage.py +30 -95
  35. sticker_convert/utils/auth/get_kakao_auth.py +7 -8
  36. sticker_convert/utils/auth/get_line_auth.py +5 -6
  37. sticker_convert/utils/auth/get_signal_auth.py +7 -7
  38. sticker_convert/utils/callback.py +31 -23
  39. sticker_convert/utils/files/cache_store.py +6 -8
  40. sticker_convert/utils/files/json_manager.py +6 -7
  41. sticker_convert/utils/files/json_resources_loader.py +12 -0
  42. sticker_convert/utils/files/metadata_handler.py +93 -84
  43. sticker_convert/utils/files/run_bin.py +11 -10
  44. sticker_convert/utils/files/sanitize_filename.py +30 -28
  45. sticker_convert/utils/media/apple_png_normalize.py +3 -2
  46. sticker_convert/utils/media/codec_info.py +41 -44
  47. sticker_convert/utils/media/decrypt_kakao.py +7 -7
  48. sticker_convert/utils/media/format_verify.py +14 -14
  49. sticker_convert/utils/url_detect.py +4 -5
  50. sticker_convert/version.py +2 -1
  51. {sticker_convert-2.7.2.dist-info → sticker_convert-2.7.4.dist-info}/METADATA +19 -17
  52. {sticker_convert-2.7.2.dist-info → sticker_convert-2.7.4.dist-info}/RECORD +56 -55
  53. {sticker_convert-2.7.2.dist-info → sticker_convert-2.7.4.dist-info}/WHEEL +1 -1
  54. {sticker_convert-2.7.2.dist-info → sticker_convert-2.7.4.dist-info}/LICENSE +0 -0
  55. {sticker_convert-2.7.2.dist-info → sticker_convert-2.7.4.dist-info}/entry_points.txt +0 -0
  56. {sticker_convert-2.7.2.dist-info → sticker_convert-2.7.4.dist-info}/top_level.txt +0 -0
@@ -1,16 +1,25 @@
1
1
  #!/usr/bin/env python3
2
2
  from multiprocessing import Event, Queue
3
- from typing import Any, Callable, Optional, Union
3
+ from typing import Any, Callable, Dict, Optional, Tuple, Union
4
4
 
5
5
  from tqdm import tqdm
6
6
 
7
+ CbQueueTupleType = Tuple[
8
+ Optional[str], Optional[Tuple[Any, ...]], Optional[Dict[str, Any]]
9
+ ]
10
+ CbQueueItemType = Union[
11
+ CbQueueTupleType,
12
+ str,
13
+ None,
14
+ ]
15
+
7
16
 
8
17
  class CallbackReturn:
9
- def __init__(self):
18
+ def __init__(self) -> None:
10
19
  self.response_event = Event()
11
20
  self.response_queue: Queue[Union[bool, str, None]] = Queue()
12
21
 
13
- def set_response(self, response: Union[bool, str, None]):
22
+ def set_response(self, response: Union[bool, str, None]) -> None:
14
23
  self.response_queue.put(response)
15
24
  self.response_event.set()
16
25
 
@@ -34,7 +43,7 @@ class Callback:
34
43
  ask_str: Optional[Callable[..., str]] = None,
35
44
  silent: bool = False,
36
45
  no_confirm: bool = False,
37
- ):
46
+ ) -> None:
38
47
  self.progress_bar: Optional[tqdm[Any]] = None
39
48
 
40
49
  if msg:
@@ -65,7 +74,7 @@ class Callback:
65
74
  self.silent = silent
66
75
  self.no_confirm = no_confirm
67
76
 
68
- def cb_msg(self, *args: Any, **kwargs: Any):
77
+ def cb_msg(self, *args: Any, **kwargs: Any) -> None:
69
78
  if self.silent:
70
79
  return
71
80
 
@@ -88,7 +97,7 @@ class Callback:
88
97
  set_progress_mode: Optional[str] = None,
89
98
  steps: int = 0,
90
99
  update_bar: bool = False,
91
- ):
100
+ ) -> None:
92
101
  if self.silent:
93
102
  return
94
103
 
@@ -103,7 +112,7 @@ class Callback:
103
112
  elif set_progress_mode == "determinate":
104
113
  self.progress_bar = tqdm(total=steps)
105
114
 
106
- def cb_msg_block(self, *args: Any):
115
+ def cb_msg_block(self, *args: Any) -> None:
107
116
  if self.silent:
108
117
  return
109
118
  if len(args) > 0:
@@ -122,16 +131,15 @@ class Callback:
122
131
  '"--no-confirm" flag is set. Continue with this run without asking questions'
123
132
  )
124
133
  return True
125
- else:
126
- self.msg(
127
- '[If you do not want to get asked by this question, add "--no-confirm" flag]'
128
- )
129
- self.msg()
130
- result = input("Continue? [y/N] > ")
131
- if result.lower() != "y":
132
- return False
133
- else:
134
- return True
134
+
135
+ self.msg(
136
+ '[If you do not want to get asked by this question, add "--no-confirm" flag]'
137
+ )
138
+ self.msg()
139
+ result = input("Continue? [y/N] > ")
140
+ if result.lower() != "y":
141
+ return False
142
+ return True
135
143
 
136
144
  def cb_ask_str(
137
145
  self,
@@ -155,29 +163,29 @@ class Callback:
155
163
  def put(
156
164
  self,
157
165
  i: Union[
158
- tuple[Optional[str], Optional[tuple[Any, ...]], Optional[dict[str, Any]]],
166
+ CbQueueItemType,
159
167
  str,
160
168
  ],
161
169
  ) -> Union[str, bool, None]:
162
170
  if isinstance(i, tuple):
163
171
  action = i[0]
164
172
  if len(i) >= 2:
165
- args: tuple[str, ...] = i[1] if i[1] else tuple()
173
+ args: Tuple[str, ...] = i[1] if i[1] else tuple()
166
174
  else:
167
175
  args = tuple()
168
176
  if len(i) >= 3:
169
- kwargs: dict[str, Any] = i[2] if i[2] else dict()
177
+ kwargs: Dict[str, Any] = i[2] if i[2] else {}
170
178
  else:
171
- kwargs = dict()
179
+ kwargs = {}
172
180
  else:
173
181
  action = i
174
182
  args = tuple()
175
- kwargs = dict()
183
+ kwargs = {}
176
184
 
177
185
  # Fake implementation for Queue.put()
178
186
  if action is None:
179
187
  return None
180
- elif action == "msg":
188
+ if action == "msg":
181
189
  self.msg(*args, **kwargs)
182
190
  elif action == "bar":
183
191
  self.bar(**kwargs)
@@ -2,7 +2,10 @@
2
2
  import os
3
3
  import platform
4
4
  import shutil
5
+ from contextlib import contextmanager
5
6
  from pathlib import Path
7
+ from tempfile import TemporaryDirectory
8
+ from typing import Any, ContextManager, Generator, Optional, Union
6
9
  from uuid import uuid4
7
10
 
8
11
  if platform.system() == "Linux":
@@ -12,14 +15,10 @@ if platform.system() == "Linux":
12
15
  else:
13
16
  import tempfile
14
17
 
15
- from contextlib import contextmanager
16
- from tempfile import TemporaryDirectory
17
- from typing import ContextManager, Optional, Union
18
-
19
18
 
20
19
  def debug_cache_dir(path: str) -> ContextManager[Path]:
21
20
  @contextmanager
22
- def generator():
21
+ def generator() -> Generator[Any, Any, Any]:
23
22
  path_random = Path(path, str(uuid4()))
24
23
  os.mkdir(path_random)
25
24
  try:
@@ -34,8 +33,7 @@ class CacheStore:
34
33
  @staticmethod
35
34
  def get_cache_store(
36
35
  path: Optional[str] = None,
37
- ) -> Union[ContextManager[Path], TemporaryDirectory[str]]:
36
+ ) -> "Union[ContextManager[Path], TemporaryDirectory[str]]":
38
37
  if path:
39
38
  return debug_cache_dir(path)
40
- else:
41
- return tempfile.TemporaryDirectory() # type: ignore
39
+ return tempfile.TemporaryDirectory() # type: ignore
@@ -1,20 +1,19 @@
1
1
  #!/usr/bin/env python3
2
2
  import json
3
3
  from pathlib import Path
4
- from typing import Any
4
+ from typing import Any, Dict
5
5
 
6
6
 
7
7
  class JsonManager:
8
8
  @staticmethod
9
- def load_json(path: Path) -> dict[Any, Any]:
9
+ def load_json(path: Path) -> Dict[Any, Any]:
10
10
  if not path.is_file():
11
11
  raise RuntimeError(f"{path} cannot be found")
12
- else:
13
- with open(path, encoding="utf-8") as f:
14
- data = json.load(f)
15
- return data
12
+ with open(path, encoding="utf-8") as f:
13
+ data = json.load(f)
14
+ return data
16
15
 
17
16
  @staticmethod
18
- def save_json(path: Path, data: dict[Any, Any]):
17
+ def save_json(path: Path, data: Dict[Any, Any]) -> None:
19
18
  with open(path, "w+", encoding="utf-8") as f:
20
19
  json.dump(data, f, indent=4)
@@ -0,0 +1,12 @@
1
+ from typing import Dict
2
+
3
+ from sticker_convert.definitions import ROOT_DIR
4
+ from sticker_convert.utils.files.json_manager import JsonManager
5
+
6
+ HELP_JSON: Dict[str, Dict[str, str]] = JsonManager.load_json(
7
+ ROOT_DIR / "resources/help.json"
8
+ )
9
+ INPUT_JSON = JsonManager.load_json(ROOT_DIR / "resources/input.json")
10
+ COMPRESSION_JSON = JsonManager.load_json(ROOT_DIR / "resources/compression.json")
11
+ OUTPUT_JSON = JsonManager.load_json(ROOT_DIR / "resources/output.json")
12
+ EMOJI_JSON = JsonManager.load_json(ROOT_DIR / "resources/emoji.json")
@@ -3,12 +3,57 @@ from __future__ import annotations
3
3
 
4
4
  import json
5
5
  from pathlib import Path
6
- from typing import Optional
6
+ from typing import Dict, List, Optional, Tuple
7
7
 
8
- from sticker_convert.definitions import ROOT_DIR
9
- from sticker_convert.utils.files.json_manager import JsonManager
8
+ from sticker_convert.utils.files.json_resources_loader import INPUT_JSON, OUTPUT_JSON
10
9
  from sticker_convert.utils.media.codec_info import CodecInfo
11
10
 
11
+ RELATED_EXTENSIONS = (
12
+ ".png",
13
+ ".apng",
14
+ ".jpg",
15
+ ".jpeg",
16
+ ".gif",
17
+ ".tgs",
18
+ ".lottie",
19
+ ".json",
20
+ ".mp4",
21
+ ".mkv",
22
+ ".mov",
23
+ ".webm",
24
+ ".webp",
25
+ ".avi",
26
+ ".m4a",
27
+ ".wastickers",
28
+ )
29
+ RELATED_NAME = (
30
+ "title.txt",
31
+ "author.txt",
32
+ "emoji.txt",
33
+ "export-result.txt",
34
+ ".DS_Store",
35
+ "._.DS_Store",
36
+ )
37
+
38
+ BLACKLIST_PREFIX = ("cover",)
39
+ BLACKLIST_SUFFIX = (".txt", ".m4a", ".wastickers", ".DS_Store", "._.DS_Store")
40
+
41
+ XCODE_IMESSAGE_ICONSET = {
42
+ "App-Store-1024x1024pt.png": (1024, 1024),
43
+ "iPad-Settings-29pt@2x.png": (58, 58),
44
+ "iPhone-settings-29pt@2x.png": (58, 58),
45
+ "iPhone-settings-29pt@3x.png": (87, 87),
46
+ "Messages27x20pt@2x.png": (54, 40),
47
+ "Messages27x20pt@3x.png": (81, 60),
48
+ "Messages32x24pt@2x.png": (64, 48),
49
+ "Messages32x24pt@3x.png": (96, 72),
50
+ "Messages-App-Store-1024x768pt.png": (1024, 768),
51
+ "Messages-iPad-67x50pt@2x.png": (134, 100),
52
+ "Messages-iPad-Pro-74x55pt@2x.png": (148, 110),
53
+ "Messages-iPhone-60x45pt@2x.png": (120, 90),
54
+ "Messages-iPhone-60x45pt@3x.png": (180, 135),
55
+ }
56
+
12
57
 
13
58
  def check_if_xcodeproj(path: Path) -> bool:
14
59
  if not path.is_dir():
@@ -22,44 +67,14 @@ def check_if_xcodeproj(path: Path) -> bool:
22
67
  class MetadataHandler:
23
68
  @staticmethod
24
69
  def get_files_related_to_sticker_convert(
25
- dir: Path, include_archive: bool = True
26
- ) -> list[Path]:
27
- from sticker_convert.uploaders.xcode_imessage import XcodeImessageIconset
28
-
29
- xcode_iconset = XcodeImessageIconset().iconset
30
- related_extensions = (
31
- ".png",
32
- ".apng",
33
- ".jpg",
34
- ".jpeg",
35
- ".gif",
36
- ".tgs",
37
- ".lottie",
38
- ".json",
39
- ".mp4",
40
- ".mkv",
41
- ".mov",
42
- ".webm",
43
- ".webp",
44
- ".avi",
45
- ".m4a",
46
- ".wastickers",
47
- )
48
- related_name = (
49
- "title.txt",
50
- "author.txt",
51
- "emoji.txt",
52
- "export-result.txt",
53
- ".DS_Store",
54
- "._.DS_Store",
55
- )
56
-
70
+ directory: Path, include_archive: bool = True
71
+ ) -> List[Path]:
57
72
  files = [
58
73
  i
59
- for i in sorted(dir.iterdir())
60
- if i.stem in related_name
61
- or i.name in xcode_iconset
62
- or i.suffix in related_extensions
74
+ for i in sorted(directory.iterdir())
75
+ if i.stem in RELATED_NAME
76
+ or i.name in XCODE_IMESSAGE_ICONSET
77
+ or i.suffix in RELATED_EXTENSIONS
63
78
  or (include_archive and i.name.startswith("archive_"))
64
79
  or check_if_xcodeproj(i)
65
80
  ]
@@ -67,51 +82,45 @@ class MetadataHandler:
67
82
  return files
68
83
 
69
84
  @staticmethod
70
- def get_stickers_present(dir: Path) -> list[Path]:
71
- from sticker_convert.uploaders.xcode_imessage import XcodeImessageIconset
72
-
73
- blacklist_prefix = ("cover",)
74
- blacklist_suffix = (".txt", ".m4a", ".wastickers", ".DS_Store", "._.DS_Store")
75
- xcode_iconset = XcodeImessageIconset().iconset
76
-
85
+ def get_stickers_present(directory: Path) -> List[Path]:
77
86
  stickers_present = [
78
87
  i
79
- for i in sorted(dir.iterdir())
80
- if Path(dir, i.name).is_file()
81
- and not i.name.startswith(blacklist_prefix)
82
- and i.suffix not in blacklist_suffix
83
- and i.name not in xcode_iconset
88
+ for i in sorted(directory.iterdir())
89
+ if Path(directory, i.name).is_file()
90
+ and not i.name.startswith(BLACKLIST_PREFIX)
91
+ and i.suffix not in BLACKLIST_SUFFIX
92
+ and i.name not in XCODE_IMESSAGE_ICONSET
84
93
  ]
85
94
 
86
95
  return stickers_present
87
96
 
88
97
  @staticmethod
89
- def get_cover(dir: Path) -> Optional[Path]:
90
- stickers_present = sorted(dir.iterdir())
98
+ def get_cover(directory: Path) -> Optional[Path]:
99
+ stickers_present = sorted(directory.iterdir())
91
100
  for i in stickers_present:
92
101
  if Path(i).stem == "cover":
93
- return Path(dir, i.name)
102
+ return Path(directory, i.name)
94
103
 
95
104
  return None
96
105
 
97
106
  @staticmethod
98
107
  def get_metadata(
99
- dir: Path,
108
+ directory: Path,
100
109
  title: Optional[str] = None,
101
110
  author: Optional[str] = None,
102
- emoji_dict: Optional[dict[str, str]] = None,
103
- ) -> tuple[Optional[str], Optional[str], Optional[dict[str, str]]]:
104
- title_path = Path(dir, "title.txt")
111
+ emoji_dict: Optional[Dict[str, str]] = None,
112
+ ) -> Tuple[Optional[str], Optional[str], Optional[Dict[str, str]]]:
113
+ title_path = Path(directory, "title.txt")
105
114
  if not title and title_path.is_file():
106
115
  with open(title_path, encoding="utf-8") as f:
107
116
  title = f.read().strip()
108
117
 
109
- author_path = Path(dir, "author.txt")
118
+ author_path = Path(directory, "author.txt")
110
119
  if not author and author_path.is_file():
111
120
  with open(author_path, encoding="utf-8") as f:
112
121
  author = f.read().strip()
113
122
 
114
- emoji_path = Path(dir, "emoji.txt")
123
+ emoji_path = Path(directory, "emoji.txt")
115
124
  if not emoji_dict and emoji_path.is_file():
116
125
  with open(emoji_path, "r", encoding="utf-8") as f:
117
126
  emoji_dict = json.load(f)
@@ -120,22 +129,22 @@ class MetadataHandler:
120
129
 
121
130
  @staticmethod
122
131
  def set_metadata(
123
- dir: Path,
132
+ directory: Path,
124
133
  title: Optional[str] = None,
125
134
  author: Optional[str] = None,
126
- emoji_dict: Optional[dict[str, str]] = None,
127
- ):
128
- title_path = Path(dir, "title.txt")
135
+ emoji_dict: Optional[Dict[str, str]] = None,
136
+ ) -> None:
137
+ title_path = Path(directory, "title.txt")
129
138
  if title is not None:
130
139
  with open(title_path, "w+", encoding="utf-8") as f:
131
140
  f.write(title)
132
141
 
133
- author_path = Path(dir, "author.txt")
142
+ author_path = Path(directory, "author.txt")
134
143
  if author is not None:
135
144
  with open(author_path, "w+", encoding="utf-8") as f:
136
145
  f.write(author)
137
146
 
138
- emoji_path = Path(dir, "emoji.txt")
147
+ emoji_path = Path(directory, "emoji.txt")
139
148
  if emoji_dict is not None:
140
149
  with open(emoji_path, "w+", encoding="utf-8") as f:
141
150
  json.dump(emoji_dict, f, indent=4, ensure_ascii=False)
@@ -150,7 +159,7 @@ class MetadataHandler:
150
159
  Does not check if metadata provided via user input in GUI or flag options
151
160
  metadata = 'title' or 'author'
152
161
  """
153
- input_presets = JsonManager.load_json(ROOT_DIR / "resources/input.json")
162
+ input_presets = INPUT_JSON
154
163
  assert input_presets
155
164
 
156
165
  if input_option == "local":
@@ -158,7 +167,7 @@ class MetadataHandler:
158
167
  metadata_provided = metadata_file_path.is_file()
159
168
  if metadata_provided:
160
169
  with open(metadata_file_path, encoding="utf-8") as f:
161
- metadata_provided = True if f.read() else False
170
+ metadata_provided = bool(f.read())
162
171
  else:
163
172
  metadata_provided = input_presets[input_option]["metadata_provides"][
164
173
  metadata
@@ -169,21 +178,21 @@ class MetadataHandler:
169
178
  @staticmethod
170
179
  def check_metadata_required(output_option: str, metadata: str) -> bool:
171
180
  # metadata = 'title' or 'author'
172
- output_presets = JsonManager.load_json(ROOT_DIR / "resources/output.json")
181
+ output_presets = OUTPUT_JSON
173
182
  assert output_presets
174
183
  return output_presets[output_option]["metadata_requirements"][metadata]
175
184
 
176
185
  @staticmethod
177
- def generate_emoji_file(dir: Path, default_emoji: str = ""):
178
- emoji_path = Path(dir, "emoji.txt")
186
+ def generate_emoji_file(directory: Path, default_emoji: str = "") -> None:
187
+ emoji_path = Path(directory, "emoji.txt")
179
188
  emoji_dict = None
180
189
  if emoji_path.is_file():
181
190
  with open(emoji_path, "r", encoding="utf-8") as f:
182
191
  emoji_dict = json.load(f)
183
192
 
184
193
  emoji_dict_new = {}
185
- for file in sorted(dir.iterdir()):
186
- if not Path(dir, file).is_file() and CodecInfo.get_file_ext(file) in (
194
+ for file in sorted(directory.iterdir()):
195
+ if not Path(directory, file).is_file() and CodecInfo.get_file_ext(file) in (
187
196
  ".txt",
188
197
  ".m4a",
189
198
  ):
@@ -199,15 +208,15 @@ class MetadataHandler:
199
208
 
200
209
  @staticmethod
201
210
  def split_sticker_packs(
202
- dir: Path,
211
+ directory: Path,
203
212
  title: str,
204
213
  file_per_pack: Optional[int] = None,
205
214
  file_per_anim_pack: Optional[int] = None,
206
215
  file_per_image_pack: Optional[int] = None,
207
216
  separate_image_anim: bool = True,
208
- ) -> dict[str, list[Path]]:
217
+ ) -> Dict[str, List[Path]]:
209
218
  # {pack_1: [sticker1_path, sticker2_path]}
210
- packs: dict[str, list[Path]] = {}
219
+ packs: Dict[str, List[Path]] = {}
211
220
 
212
221
  if file_per_pack is None:
213
222
  file_per_pack = (
@@ -219,13 +228,13 @@ class MetadataHandler:
219
228
  file_per_anim_pack = file_per_pack
220
229
  file_per_image_pack = file_per_pack
221
230
 
222
- stickers_present = MetadataHandler.get_stickers_present(dir)
231
+ stickers_present = MetadataHandler.get_stickers_present(directory)
223
232
 
224
233
  processed = 0
225
234
 
226
235
  if separate_image_anim is True:
227
- image_stickers: list[Path] = []
228
- anim_stickers: list[Path] = []
236
+ image_stickers: List[Path] = []
237
+ anim_stickers: List[Path] = []
229
238
 
230
239
  image_pack_count = 0
231
240
  anim_pack_count = 0
@@ -234,7 +243,7 @@ class MetadataHandler:
234
243
  image_present = False
235
244
 
236
245
  for processed, file in enumerate(stickers_present):
237
- file_path = dir / file
246
+ file_path = directory / file
238
247
 
239
248
  if CodecInfo.is_anim(file_path):
240
249
  anim_stickers.append(file_path)
@@ -244,7 +253,7 @@ class MetadataHandler:
244
253
  anim_present = anim_present or len(anim_stickers) > 0
245
254
  image_present = image_present or len(image_stickers) > 0
246
255
 
247
- finished_all = True if processed == len(stickers_present) - 1 else False
256
+ finished_all = processed == len(stickers_present) - 1
248
257
 
249
258
  if len(anim_stickers) == file_per_anim_pack or (
250
259
  finished_all and len(anim_stickers) > 0
@@ -264,15 +273,15 @@ class MetadataHandler:
264
273
  image_pack_count += 1
265
274
 
266
275
  else:
267
- stickers: list[Path] = []
276
+ stickers: List[Path] = []
268
277
  pack_count = 0
269
278
 
270
279
  for processed, file in enumerate(stickers_present):
271
- file_path = Path(dir, file)
280
+ file_path = Path(directory, file)
272
281
 
273
282
  stickers.append(file_path)
274
283
 
275
- finished_all = True if processed == len(stickers_present) - 1 else False
284
+ finished_all = processed == len(stickers_present) - 1
276
285
 
277
286
  if len(stickers) == file_per_pack or (
278
287
  finished_all and len(stickers) > 0
@@ -3,32 +3,32 @@ import platform
3
3
  import shutil
4
4
  import subprocess
5
5
  from pathlib import Path
6
- from typing import Any, Callable, Union
6
+ from typing import Any, Callable, List, Tuple, Union
7
7
 
8
8
 
9
9
  class RunBin:
10
10
  @staticmethod
11
11
  def get_bin(
12
- bin: str, silent: bool = False, cb_msg: Callable[..., Any] = print
12
+ executable: str, silent: bool = False, cb_msg: Callable[..., Any] = print
13
13
  ) -> Union[str, None]:
14
- if Path(bin).is_file():
15
- return bin
14
+ if Path(executable).is_file():
15
+ return executable
16
16
 
17
17
  if platform.system() == "Windows":
18
- bin = bin + ".exe"
18
+ executable = executable + ".exe"
19
19
 
20
- which_result = shutil.which(bin)
20
+ which_result = shutil.which(executable)
21
21
  if which_result is not None:
22
22
  return str(Path(which_result).resolve())
23
- elif silent is False:
24
- cb_msg(f"Warning: Cannot find binary file {bin}")
23
+ if silent is False:
24
+ cb_msg(f"Warning: Cannot find binary file {executable}")
25
25
 
26
26
  return None
27
27
 
28
28
  @staticmethod
29
29
  def run_cmd(
30
- cmd_list: list[str], silence: bool = False, cb_msg: Callable[..., Any] = print
31
- ) -> tuple[bool, str]:
30
+ cmd_list: List[str], silence: bool = False, cb_msg: Callable[..., Any] = print
31
+ ) -> Tuple[bool, str]:
32
32
  bin_path = RunBin.get_bin(cmd_list[0])
33
33
 
34
34
  if bin_path:
@@ -46,6 +46,7 @@ class RunBin:
46
46
  stdin=subprocess.PIPE,
47
47
  stdout=subprocess.PIPE,
48
48
  stderr=subprocess.PIPE,
49
+ check=False,
49
50
  )
50
51
 
51
52
  output_str = sp.stdout.decode()
@@ -2,6 +2,32 @@
2
2
  import re
3
3
  import unicodedata
4
4
 
5
+ BLACKLIST_CHAR = ("\\", "/", ":", "*", "?", '"', "<", ">", "|", "\0")
6
+ RESERVED_FILENAME = (
7
+ "CON",
8
+ "PRN",
9
+ "AUX",
10
+ "NUL",
11
+ "COM1",
12
+ "COM2",
13
+ "COM3",
14
+ "COM4",
15
+ "COM5",
16
+ "COM6",
17
+ "COM7",
18
+ "COM8",
19
+ "COM9",
20
+ "LPT1",
21
+ "LPT2",
22
+ "LPT3",
23
+ "LPT4",
24
+ "LPT5",
25
+ "LPT6",
26
+ "LPT7",
27
+ "LPT8",
28
+ "LPT9",
29
+ ) # Reserved words on Windows
30
+
5
31
 
6
32
  def sanitize_filename(filename: str) -> str:
7
33
  # Based on https://gitlab.com/jplusplus/sanitize-filename/-/blob/master/sanitize_filename/sanitize_filename.py
@@ -13,40 +39,16 @@ def sanitize_filename(filename: str) -> str:
13
39
  and make sure we do not exceed Windows filename length limits.
14
40
  Hence a less safe blacklist, rather than a whitelist.
15
41
  """
16
- blacklist = ["\\", "/", ":", "*", "?", '"', "<", ">", "|", "\0"]
17
- reserved = [
18
- "CON",
19
- "PRN",
20
- "AUX",
21
- "NUL",
22
- "COM1",
23
- "COM2",
24
- "COM3",
25
- "COM4",
26
- "COM5",
27
- "COM6",
28
- "COM7",
29
- "COM8",
30
- "COM9",
31
- "LPT1",
32
- "LPT2",
33
- "LPT3",
34
- "LPT4",
35
- "LPT5",
36
- "LPT6",
37
- "LPT7",
38
- "LPT8",
39
- "LPT9",
40
- ] # Reserved words on Windows
41
- filename = "".join(c if c not in blacklist else "_" for c in filename)
42
+
43
+ filename = "".join(c if c not in BLACKLIST_CHAR else "_" for c in filename)
42
44
  # Remove all charcters below code point 32
43
45
  filename = "".join(c if 31 < ord(c) else "_" for c in filename)
44
46
  filename = unicodedata.normalize("NFKD", filename)
45
47
  filename = filename.rstrip(". ") # Windows does not allow these at end
46
48
  filename = filename.strip()
47
- if all([x == "." for x in filename]):
49
+ if all(x == "." for x in filename):
48
50
  filename = "__" + filename
49
- if filename in reserved:
51
+ if filename in RESERVED_FILENAME:
50
52
  filename = "__" + filename
51
53
  if len(filename) == 0:
52
54
  filename = "__"
@@ -3,6 +3,7 @@
3
3
  Required for pack_id < 775 (https://github.com/star-39/moe-sticker-bot/blob/ef2f06f3eb7a833e011e0a5201e007fc130978e7/pkg/msbimport/import_line.go#L68)
4
4
  Reference: https://stackoverflow.com/a/53622211
5
5
  """
6
+
6
7
  import struct
7
8
  import zlib
8
9
 
@@ -55,8 +56,8 @@ class ApplePngNormalize:
55
56
 
56
57
  assert width
57
58
  assert height
58
- bufSize = width * height * 4 + height
59
- chunk_data = zlib.decompress(chunk_d, -8, bufSize)
59
+ buf_size = width * height * 4 + height
60
+ chunk_data = zlib.decompress(chunk_d, -8, buf_size)
60
61
 
61
62
  # Swapping red & blue bytes for each pixel
62
63
  chunk_data = bytearray(chunk_data)