sticker-convert 2.1.5__py3-none-any.whl → 2.1.7__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 (57) hide show
  1. sticker_convert/__init__.py +1 -1
  2. sticker_convert/__main__.py +7 -4
  3. sticker_convert/cli.py +42 -32
  4. sticker_convert/converter.py +432 -0
  5. sticker_convert/downloaders/download_base.py +40 -16
  6. sticker_convert/downloaders/download_kakao.py +103 -136
  7. sticker_convert/downloaders/download_line.py +30 -12
  8. sticker_convert/downloaders/download_signal.py +48 -32
  9. sticker_convert/downloaders/download_telegram.py +71 -26
  10. sticker_convert/gui.py +79 -130
  11. sticker_convert/{gui_frames → gui_components/frames}/comp_frame.py +2 -3
  12. sticker_convert/{gui_frames → gui_components/frames}/config_frame.py +3 -4
  13. sticker_convert/{gui_frames → gui_components/frames}/control_frame.py +2 -2
  14. sticker_convert/{gui_frames → gui_components/frames}/cred_frame.py +4 -4
  15. sticker_convert/{gui_frames → gui_components/frames}/input_frame.py +4 -4
  16. sticker_convert/{gui_frames → gui_components/frames}/output_frame.py +3 -3
  17. sticker_convert/{gui_frames → gui_components/frames}/progress_frame.py +1 -1
  18. sticker_convert/{utils → gui_components}/gui_utils.py +38 -21
  19. sticker_convert/{gui_windows → gui_components/windows}/advanced_compression_window.py +3 -2
  20. sticker_convert/{gui_windows → gui_components/windows}/base_window.py +3 -2
  21. sticker_convert/{gui_windows → gui_components/windows}/kakao_get_auth_window.py +3 -3
  22. sticker_convert/{gui_windows → gui_components/windows}/line_get_auth_window.py +2 -2
  23. sticker_convert/{gui_windows → gui_components/windows}/signal_get_auth_window.py +2 -2
  24. sticker_convert/{flow.py → job.py} +91 -102
  25. sticker_convert/job_option.py +301 -0
  26. sticker_convert/resources/compression.json +1 -1
  27. sticker_convert/uploaders/compress_wastickers.py +95 -74
  28. sticker_convert/uploaders/upload_base.py +16 -4
  29. sticker_convert/uploaders/upload_signal.py +100 -62
  30. sticker_convert/uploaders/upload_telegram.py +168 -128
  31. sticker_convert/uploaders/xcode_imessage.py +202 -132
  32. sticker_convert/{auth → utils/auth}/get_kakao_auth.py +7 -5
  33. sticker_convert/{auth → utils/auth}/get_line_auth.py +6 -5
  34. sticker_convert/{auth → utils/auth}/get_signal_auth.py +1 -1
  35. sticker_convert/utils/fake_cb_msg.py +5 -2
  36. sticker_convert/utils/{cache_store.py → files/cache_store.py} +7 -3
  37. sticker_convert/utils/files/dir_utils.py +64 -0
  38. sticker_convert/utils/{json_manager.py → files/json_manager.py} +5 -4
  39. sticker_convert/utils/files/metadata_handler.py +226 -0
  40. sticker_convert/utils/files/run_bin.py +58 -0
  41. sticker_convert/utils/{apple_png_normalize.py → media/apple_png_normalize.py} +23 -20
  42. sticker_convert/utils/{codec_info.py → media/codec_info.py} +41 -35
  43. sticker_convert/utils/media/decrypt_kakao.py +68 -0
  44. sticker_convert/utils/media/format_verify.py +184 -0
  45. sticker_convert/utils/url_detect.py +16 -14
  46. {sticker_convert-2.1.5.dist-info → sticker_convert-2.1.7.dist-info}/METADATA +11 -11
  47. {sticker_convert-2.1.5.dist-info → sticker_convert-2.1.7.dist-info}/RECORD +52 -50
  48. {sticker_convert-2.1.5.dist-info → sticker_convert-2.1.7.dist-info}/WHEEL +1 -1
  49. sticker_convert/utils/converter.py +0 -399
  50. sticker_convert/utils/curr_dir.py +0 -70
  51. sticker_convert/utils/format_verify.py +0 -188
  52. sticker_convert/utils/metadata_handler.py +0 -190
  53. sticker_convert/utils/run_bin.py +0 -46
  54. /sticker_convert/{gui_frames → gui_components/frames}/right_clicker.py +0 -0
  55. {sticker_convert-2.1.5.dist-info → sticker_convert-2.1.7.dist-info}/LICENSE +0 -0
  56. {sticker_convert-2.1.5.dist-info → sticker_convert-2.1.7.dist-info}/entry_points.txt +0 -0
  57. {sticker_convert-2.1.5.dist-info → sticker_convert-2.1.7.dist-info}/top_level.txt +0 -0
@@ -1,21 +1,35 @@
1
1
  #!/usr/bin/env python3
2
+ from __future__ import annotations
2
3
  from typing import Optional, Union
3
4
 
4
5
  import requests
5
6
 
7
+ from ..job_option import CredOption # type: ignore
8
+
9
+
6
10
  class DownloadBase:
7
- def __init__(self, url: str, out_dir: str, opt_cred: Optional[dict] = None, cb_msg=print, cb_msg_block=input, cb_bar=None):
11
+ def __init__(
12
+ self,
13
+ url: str,
14
+ out_dir: str,
15
+ opt_cred: Optional[CredOption] = None,
16
+ cb_msg=print,
17
+ cb_msg_block=input,
18
+ cb_bar=None,
19
+ ):
8
20
  self.url = url
9
21
  self.out_dir = out_dir
10
22
  self.opt_cred = opt_cred
11
23
  self.cb_msg = cb_msg
12
24
  self.cb_msg_block = cb_msg_block
13
25
  self.cb_bar = cb_bar
14
-
15
- def download_multiple_files(self, targets: list[tuple[str, str]], retries: int = 3, **kwargs):
26
+
27
+ def download_multiple_files(
28
+ self, targets: list[tuple[str, str]], retries: int = 3, **kwargs
29
+ ):
16
30
  # targets format: [(url1, dest2), (url2, dest2), ...]
17
31
  if self.cb_bar:
18
- self.cb_bar(set_progress_mode='determinate', steps=len(targets))
32
+ self.cb_bar(set_progress_mode="determinate", steps=len(targets))
19
33
 
20
34
  for url, dest in targets:
21
35
  self.download_file(url, dest, retries, show_progress=False, **kwargs)
@@ -23,39 +37,49 @@ class DownloadBase:
23
37
  if self.cb_bar:
24
38
  self.cb_bar(update_bar=True)
25
39
 
26
- def download_file(self, url: str, dest: Optional[str] = None, retries: int = 3, show_progress: bool = True, **kwargs) -> Union[bool, bytes]:
27
- result = b''
40
+ def download_file(
41
+ self,
42
+ url: str,
43
+ dest: Optional[str] = None,
44
+ retries: int = 3,
45
+ show_progress: bool = True,
46
+ **kwargs,
47
+ ) -> Union[bool, bytes]:
48
+ result = b""
28
49
  chunk_size = 102400
29
50
 
30
51
  for retry in range(retries):
31
52
  try:
32
53
  response = requests.get(url, stream=True, **kwargs)
33
- total_length = int(response.headers.get('content-length')) # type: ignore[arg-type]
54
+ total_length = int(response.headers.get("content-length")) # type: ignore[arg-type]
34
55
 
35
56
  if response.status_code != 200:
36
57
  return False
37
58
  else:
38
- self.cb_msg(f'Downloading {url}')
59
+ self.cb_msg(f"Downloading {url}")
39
60
 
40
61
  if self.cb_bar and show_progress:
41
- self.cb_bar(set_progress_mode='determinate', steps=(total_length/chunk_size) + 1)
62
+ self.cb_bar(
63
+ set_progress_mode="determinate",
64
+ steps=(total_length / chunk_size) + 1,
65
+ )
42
66
 
43
67
  for chunk in response.iter_content(chunk_size=chunk_size):
44
68
  if chunk:
45
69
  result += chunk
46
70
  if self.cb_bar and show_progress:
47
71
  self.cb_bar(update_bar=True)
48
-
72
+
49
73
  break
50
74
  except requests.exceptions.RequestException:
51
- self.cb_msg(f'Cannot download {url} (tried {retry+1}/{retries} times)')
52
-
75
+ self.cb_msg(f"Cannot download {url} (tried {retry+1}/{retries} times)")
76
+
53
77
  if not result:
54
78
  return False
55
79
  elif dest:
56
- with open(dest, 'wb+') as f:
80
+ with open(dest, "wb+") as f:
57
81
  f.write(result)
58
- self.cb_msg(f'Downloaded {url}')
82
+ self.cb_msg(f"Downloaded {url}")
59
83
  return True
60
- else:
61
- return result
84
+ else:
85
+ return result
@@ -1,10 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- '''
3
- References:
4
- https://github.com/blluv/KakaoTalkEmoticonDownloader
5
- https://github.com/star-39/moe-sticker-bot
6
- '''
7
-
2
+ from __future__ import annotations
8
3
  import requests
9
4
  from bs4 import BeautifulSoup
10
5
  import json
@@ -14,146 +9,95 @@ import io
14
9
  from urllib.parse import urlparse
15
10
  from typing import Optional
16
11
 
17
- from .download_base import DownloadBase # type: ignore
18
- from ..utils.metadata_handler import MetadataHandler # type: ignore
19
-
20
- class DecryptKakao:
21
- @staticmethod
22
- def generate_lfsr(key: str) -> list[int]:
23
- d = list(key*2)
24
- seq=[0,0,0]
25
-
26
- seq[0] = 301989938
27
- seq[1] = 623357073
28
- seq[2] = -2004086252
29
-
30
- i = 0
31
-
32
- for i in range(0, 4):
33
- seq[0] = ord(d[i]) | (seq[0] << 8)
34
- seq[1] = ord(d[4+i]) | (seq[1] << 8)
35
- seq[2] = ord(d[8+i]) | (seq[2] << 8)
36
-
37
- seq[0] = seq[0] & 0xffffffff
38
- seq[1] = seq[1] & 0xffffffff
39
- seq[2] = seq[2] & 0xffffffff
40
-
41
- return seq
12
+ from .download_base import DownloadBase # type: ignore
13
+ from ..utils.files.metadata_handler import MetadataHandler # type: ignore
14
+ from ..utils.media.decrypt_kakao import DecryptKakao # type: ignore
15
+ from ..job_option import CredOption # type: ignore
42
16
 
43
- @staticmethod
44
- def xor_byte(b: int, seq: list) -> int:
45
- flag1=1
46
- flag2=0
47
- result=0
48
- for _ in range(0, 8):
49
- v10 = (seq[0] >> 1)
50
- if (seq[0] << 31) & 0xffffffff:
51
- seq[0] = (v10 ^ 0xC0000031)
52
- v12 = (seq[1] >> 1)
53
- if (seq[1] << 31) & 0xffffffff:
54
- seq[1] = ((v12 | 0xC0000000) ^ 0x20000010)
55
- flag1 = 1
56
- else:
57
- seq[1] = (v12 & 0x3FFFFFFF)
58
- flag1 = 0
59
- else:
60
- seq[0] = v10
61
- v11 = (seq[2] >> 1)
62
- if (seq[2] << 31) & 0xffffffff:
63
- seq[2] = ((v11 | 0xF0000000) ^ 0x8000001)
64
- flag2 = 1
65
- else:
66
- seq[2] = (v11 & 0xFFFFFFF)
67
- flag2 = 0
68
-
69
- result = (flag1 ^ flag2 | 2 * result)
70
- return (result ^ b)
71
-
72
- @staticmethod
73
- def xor_data(data: bytes) -> bytes:
74
- dat = list(data)
75
- s = DecryptKakao.generate_lfsr("a271730728cbe141e47fd9d677e9006d")
76
- for i in range(0,128):
77
- dat[i] = DecryptKakao.xor_byte(dat[i], s)
78
- return bytes(dat)
79
17
 
80
18
  class MetadataKakao:
81
19
  @staticmethod
82
20
  def get_info_from_share_link(url: str) -> tuple[Optional[str], Optional[str]]:
83
- headers = {
84
- 'User-Agent': 'Android'
85
- }
21
+ headers = {"User-Agent": "Android"}
86
22
 
87
23
  response = requests.get(url, headers=headers)
88
- soup = BeautifulSoup(response.text, 'html.parser')
89
-
90
- pack_title_tag = soup.find('title')
24
+ soup = BeautifulSoup(response.text, "html.parser")
25
+
26
+ pack_title_tag = soup.find("title")
91
27
  if not pack_title_tag:
92
28
  return None, None
93
-
94
- pack_title = pack_title_tag.string # type: ignore[union-attr]
95
29
 
96
- data_url = soup.find('a', id='app_scheme_link').get('data-url') # type: ignore[union-attr]
30
+ pack_title = pack_title_tag.string # type: ignore[union-attr]
31
+
32
+ data_url = soup.find("a", id="app_scheme_link").get("data-url") # type: ignore[union-attr]
97
33
  if not data_url:
98
34
  return None, None
99
-
100
- item_code = data_url.replace('kakaotalk://store/emoticon/', '').split('?')[0] # type: ignore[union-attr]
35
+
36
+ item_code = data_url.replace("kakaotalk://store/emoticon/", "").split("?")[0] # type: ignore[union-attr]
101
37
 
102
38
  return pack_title, item_code
103
39
 
104
40
  @staticmethod
105
- def get_info_from_pack_title(pack_title: str) -> tuple[Optional[str], Optional[str], Optional[str]]:
106
- pack_meta_r = requests.get(f'https://e.kakao.com/api/v1/items/t/{pack_title}')
41
+ def get_info_from_pack_title(
42
+ pack_title: str,
43
+ ) -> tuple[Optional[str], Optional[str], Optional[str]]:
44
+ pack_meta_r = requests.get(f"https://e.kakao.com/api/v1/items/t/{pack_title}")
107
45
 
108
46
  if pack_meta_r.status_code == 200:
109
47
  pack_meta = json.loads(pack_meta_r.text)
110
48
  else:
111
49
  return None, None, None
112
50
 
113
- author = pack_meta['result']['artist']
114
- title_ko = pack_meta['result']['title']
115
- thumbnail_urls = pack_meta['result']['thumbnailUrls']
51
+ author = pack_meta["result"]["artist"]
52
+ title_ko = pack_meta["result"]["title"]
53
+ thumbnail_urls = pack_meta["result"]["thumbnailUrls"]
116
54
 
117
55
  return author, title_ko, thumbnail_urls
118
56
 
119
57
  @staticmethod
120
58
  def get_item_code(title_ko: str, auth_token: str) -> Optional[str]:
121
59
  headers = {
122
- 'Authorization': auth_token,
60
+ "Authorization": auth_token,
123
61
  }
124
62
 
125
- data = {
126
- 'query': title_ko
127
- }
63
+ data = {"query": title_ko}
64
+
65
+ response = requests.post(
66
+ "https://talk-pilsner.kakao.com/emoticon/item_store/instant_search",
67
+ headers=headers,
68
+ data=data,
69
+ )
128
70
 
129
- response = requests.post('https://talk-pilsner.kakao.com/emoticon/item_store/instant_search', headers=headers, data=data)
130
-
131
71
  if response.status_code != 200:
132
72
  return None
133
73
 
134
74
  response_json = json.loads(response.text)
135
- item_code = response_json['emoticons'][0]['item_code']
75
+ item_code = response_json["emoticons"][0]["item_code"]
136
76
 
137
77
  return item_code
138
78
 
139
79
  @staticmethod
140
80
  def get_title_from_id(item_code: str, auth_token: str) -> Optional[str]:
141
81
  headers = {
142
- 'Authorization': auth_token,
82
+ "Authorization": auth_token,
143
83
  }
144
84
 
145
- response = requests.post(f'https://talk-pilsner.kakao.com/emoticon/api/store/v3/items/{item_code}', headers=headers)
85
+ response = requests.post(
86
+ f"https://talk-pilsner.kakao.com/emoticon/api/store/v3/items/{item_code}",
87
+ headers=headers,
88
+ )
146
89
 
147
90
  if response.status_code != 200:
148
91
  return None
149
-
92
+
150
93
  response_json = json.loads(response.text)
151
- title = response_json['itemUnitInfo'][0]['title']
94
+ title = response_json["itemUnitInfo"][0]["title"]
152
95
  # play_path_format = response_json['itemUnitInfo'][0]['playPathFormat']
153
96
  # stickers_count = len(response_json['itemUnitInfo'][0]['sizes'])
154
97
 
155
98
  return title
156
99
 
100
+
157
101
  class DownloadKakao(DownloadBase):
158
102
  def __init__(self, *args, **kwargs):
159
103
  super(DownloadKakao, self).__init__(*args, **kwargs)
@@ -162,46 +106,58 @@ class DownloadKakao(DownloadBase):
162
106
 
163
107
  def download_stickers_kakao(self) -> bool:
164
108
  if self.opt_cred:
165
- auth_token = self.opt_cred.get('kakao', {}).get('auth_token')
109
+ auth_token = self.opt_cred.kakao_auth_token
166
110
 
167
- if urlparse(self.url).netloc == 'emoticon.kakao.com':
168
- self.pack_title, item_code = MetadataKakao.get_info_from_share_link(self.url)
111
+ if urlparse(self.url).netloc == "emoticon.kakao.com":
112
+ self.pack_title, item_code = MetadataKakao.get_info_from_share_link(
113
+ self.url
114
+ )
169
115
 
170
116
  if item_code:
171
117
  return self.download_animated(item_code)
172
118
  else:
173
- self.cb_msg('Download failed: Cannot download metadata for sticker pack')
119
+ self.cb_msg(
120
+ "Download failed: Cannot download metadata for sticker pack"
121
+ )
174
122
  return False
175
123
 
176
- elif self.url.isnumeric() or self.url.startswith('kakaotalk://store/emoticon/'):
177
- item_code = self.url.replace('kakaotalk://store/emoticon/', '')
124
+ elif self.url.isnumeric() or self.url.startswith("kakaotalk://store/emoticon/"):
125
+ item_code = self.url.replace("kakaotalk://store/emoticon/", "")
178
126
 
179
127
  self.pack_title = None
180
128
  if auth_token:
181
- self.pack_title = MetadataKakao.get_title_from_id(item_code, auth_token) # type: ignore[arg-type]
129
+ self.pack_title = MetadataKakao.get_title_from_id(item_code, auth_token) # type: ignore[arg-type]
182
130
  if not self.pack_title:
183
- self.cb_msg('Warning: Cannot get pack_title with auth_token.')
184
- self.cb_msg('Is auth_token invalid / expired? Try to regenerate it.')
185
- self.cb_msg('Continuing without getting pack_title')
186
-
187
- return self.download_animated(item_code) # type: ignore[arg-type]
188
-
189
- elif urlparse(self.url).netloc == 'e.kakao.com':
190
- self.pack_title = self.url.replace('https://e.kakao.com/t/', '')
191
- self.author, title_ko, thumbnail_urls = MetadataKakao.get_info_from_pack_title(self.pack_title)
131
+ self.cb_msg("Warning: Cannot get pack_title with auth_token.")
132
+ self.cb_msg(
133
+ "Is auth_token invalid / expired? Try to regenerate it."
134
+ )
135
+ self.cb_msg("Continuing without getting pack_title")
136
+
137
+ return self.download_animated(item_code) # type: ignore[arg-type]
138
+
139
+ elif urlparse(self.url).netloc == "e.kakao.com":
140
+ self.pack_title = self.url.replace("https://e.kakao.com/t/", "")
141
+ (
142
+ self.author,
143
+ title_ko,
144
+ thumbnail_urls,
145
+ ) = MetadataKakao.get_info_from_pack_title(self.pack_title)
192
146
 
193
147
  if not thumbnail_urls:
194
- self.cb_msg('Download failed: Cannot download metadata for sticker pack')
148
+ self.cb_msg(
149
+ "Download failed: Cannot download metadata for sticker pack"
150
+ )
195
151
  return False
196
152
 
197
153
  if auth_token:
198
- item_code = MetadataKakao.get_item_code(title_ko, auth_token) # type: ignore[arg-type]
154
+ item_code = MetadataKakao.get_item_code(title_ko, auth_token) # type: ignore[arg-type]
199
155
  if item_code:
200
156
  return self.download_animated(item_code)
201
157
  else:
202
- msg = 'Warning: Cannot get item code.\n'
203
- msg += 'Is auth_token invalid / expired? Try to regenerate it.\n'
204
- msg += 'Continue to download static stickers instead?'
158
+ msg = "Warning: Cannot get item code.\n"
159
+ msg += "Is auth_token invalid / expired? Try to regenerate it.\n"
160
+ msg += "Continue to download static stickers instead?"
205
161
  response = self.cb_msg_block(msg)
206
162
  if response == False:
207
163
  return False
@@ -209,66 +165,77 @@ class DownloadKakao(DownloadBase):
209
165
  return self.download_static(thumbnail_urls)
210
166
 
211
167
  else:
212
- self.cb_msg('Download failed: Unrecognized URL')
168
+ self.cb_msg("Download failed: Unrecognized URL")
213
169
  return False
214
170
 
215
171
  def download_static(self, thumbnail_urls: str) -> bool:
216
- MetadataHandler.set_metadata(self.out_dir, title=self.pack_title, author=self.author)
172
+ MetadataHandler.set_metadata(
173
+ self.out_dir, title=self.pack_title, author=self.author
174
+ )
217
175
 
218
176
  targets = []
219
177
 
220
178
  num = 0
221
179
  for url in thumbnail_urls:
222
- dest = os.path.join(self.out_dir, str(num).zfill(3) + '.png')
180
+ dest = os.path.join(self.out_dir, str(num).zfill(3) + ".png")
223
181
  targets.append((url, dest))
224
182
  num += 1
225
183
 
226
184
  self.download_multiple_files(targets)
227
-
185
+
228
186
  return True
229
-
187
+
230
188
  def download_animated(self, item_code: str) -> bool:
231
- MetadataHandler.set_metadata(self.out_dir, title=self.pack_title, author=self.author)
189
+ MetadataHandler.set_metadata(
190
+ self.out_dir, title=self.pack_title, author=self.author
191
+ )
232
192
 
233
193
  pack_url = f"http://item.kakaocdn.net/dw/{item_code}.file_pack.zip"
234
194
 
235
195
  zip_file = self.download_file(pack_url)
236
196
  if zip_file:
237
- self.cb_msg(f'Downloaded {pack_url}')
197
+ self.cb_msg(f"Downloaded {pack_url}")
238
198
  else:
239
- self.cb_msg(f'Cannot download {pack_url}')
199
+ self.cb_msg(f"Cannot download {pack_url}")
240
200
  return False
241
-
201
+
242
202
  num = 0
243
203
  with zipfile.ZipFile(io.BytesIO(zip_file)) as zf:
244
- self.cb_msg('Unzipping...')
204
+ self.cb_msg("Unzipping...")
245
205
  if self.cb_bar:
246
- self.cb_bar(set_progress_mode='determinate', steps=len(zf.namelist()))
206
+ self.cb_bar(set_progress_mode="determinate", steps=len(zf.namelist()))
247
207
 
248
208
  for f_path in sorted(zf.namelist()):
249
209
  _, ext = os.path.splitext(f_path)
250
210
 
251
- if ext in ('.gif', '.webp'):
211
+ if ext in (".gif", ".webp"):
252
212
  data = DecryptKakao.xor_data(zf.read(f_path))
253
- self.cb_msg(f'Decrypted {f_path}')
213
+ self.cb_msg(f"Decrypted {f_path}")
254
214
  else:
255
215
  data = zf.read(f_path)
256
- self.cb_msg(f'Read {f_path}')
257
-
216
+ self.cb_msg(f"Read {f_path}")
217
+
258
218
  out_path = os.path.join(self.out_dir, str(num).zfill(3) + ext)
259
- with open(out_path, 'wb') as f:
219
+ with open(out_path, "wb") as f:
260
220
  f.write(data)
261
-
221
+
262
222
  if self.cb_bar:
263
223
  self.cb_bar(update_bar=True)
264
224
 
265
225
  num += 1
266
226
 
267
- self.cb_msg(f'Finished getting {pack_url}')
268
-
227
+ self.cb_msg(f"Finished getting {pack_url}")
228
+
269
229
  return True
270
230
 
271
231
  @staticmethod
272
- def start(url: str, out_dir: str, opt_cred: Optional[dict] = None, cb_msg=print, cb_msg_block=input, cb_bar=None) -> bool:
232
+ def start(
233
+ url: str,
234
+ out_dir: str,
235
+ opt_cred: Optional[CredOption] = None,
236
+ cb_msg=print,
237
+ cb_msg_block=input,
238
+ cb_bar=None,
239
+ ) -> bool:
273
240
  downloader = DownloadKakao(url, out_dir, opt_cred, cb_msg, cb_msg_block, cb_bar)
274
- return downloader.download_stickers_kakao()
241
+ return downloader.download_stickers_kakao()
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env python3
2
+ from __future__ import annotations
2
3
  '''Reference: https://github.com/doubleplusc/Line-sticker-downloader/blob/master/sticker_dl.py'''
3
4
 
4
5
  import requests
@@ -13,9 +14,11 @@ from bs4 import BeautifulSoup
13
14
  from typing import Optional
14
15
 
15
16
  from .download_base import DownloadBase # type: ignore
16
- from ..auth.get_line_auth import GetLineAuth # type: ignore
17
- from ..utils.metadata_handler import MetadataHandler # type: ignore
18
- from ..utils.apple_png_normalize import ApplePngNormalize # type: ignore
17
+ from ..utils.auth.get_line_auth import GetLineAuth # type: ignore
18
+ from ..utils.files.metadata_handler import MetadataHandler # type: ignore
19
+ from ..utils.media.apple_png_normalize import ApplePngNormalize # type: ignore
20
+ from ..job_option import CredOption # type: ignore
21
+
19
22
 
20
23
  class MetadataLine:
21
24
  @staticmethod
@@ -142,17 +145,25 @@ class DownloadLine(DownloadBase):
142
145
 
143
146
  def load_cookies(self) -> dict[str, str]:
144
147
  cookies = {}
145
- if self.opt_cred and self.opt_cred.get('line', {}).get('cookies'):
148
+ if self.opt_cred and self.opt_cred.line_cookies:
149
+ line_cookies = self.opt_cred.line_cookies
150
+
146
151
  try:
147
- for i in self.opt_cred['line']['cookies'].split(';'):
148
- c_key, c_value = i.split('=')
149
- cookies[c_key] = c_value
150
- if not GetLineAuth.validate_cookies(cookies):
152
+ line_cookies_dict = json.loads(line_cookies)
153
+ for c in line_cookies_dict:
154
+ cookies[c['name']] = c['value']
155
+ except json.decoder.JSONDecodeError:
156
+ try:
157
+ for i in line_cookies.split(';'):
158
+ c_key, c_value = i.split('=')
159
+ cookies[c_key] = c_value
160
+ except ValueError:
151
161
  self.cb_msg('Warning: Line cookies invalid, you will not be able to add text to "Custom stickers"')
152
- cookies = {}
153
- except ValueError:
154
- self.cb_msg('Warning: Line cookies invalid, you will not be able to add text to "Custom stickers"')
155
162
 
163
+ if not GetLineAuth.validate_cookies(cookies):
164
+ self.cb_msg('Warning: Line cookies invalid, you will not be able to add text to "Custom stickers"')
165
+ cookies = {}
166
+
156
167
  return cookies
157
168
 
158
169
  def get_pack_url(self) -> str:
@@ -372,6 +383,13 @@ class DownloadLine(DownloadBase):
372
383
  return True
373
384
 
374
385
  @staticmethod
375
- def start(url: str, out_dir: str, opt_cred: Optional[dict] = None, cb_msg=print, cb_msg_block=input, cb_bar=None) -> bool:
386
+ def start(
387
+ url: str,
388
+ out_dir: str,
389
+ opt_cred: Optional[CredOption] = None,
390
+ cb_msg=print,
391
+ cb_msg_block=input,
392
+ cb_bar=None,
393
+ ) -> bool:
376
394
  downloader = DownloadLine(url, out_dir, opt_cred, cb_msg, cb_msg_block, cb_bar)
377
395
  return downloader.download_stickers_line()
@@ -3,71 +3,87 @@ import os
3
3
  from typing import Optional
4
4
 
5
5
  import anyio
6
- from signalstickers_client import StickersClient # type: ignore
7
- from signalstickers_client.models import StickerPack # type: ignore
6
+ from signalstickers_client import StickersClient # type: ignore
7
+ from signalstickers_client.models import StickerPack # type: ignore
8
+
9
+ from .download_base import DownloadBase # type: ignore
10
+ from ..utils.files.metadata_handler import MetadataHandler # type: ignore
11
+ from ..utils.media.codec_info import CodecInfo # type: ignore
12
+ from ..job_option import CredOption # type: ignore
8
13
 
9
- from .download_base import DownloadBase # type: ignore
10
- from ..utils.metadata_handler import MetadataHandler # type: ignore
11
- from ..utils.codec_info import CodecInfo # type: ignore
12
14
 
13
15
  class DownloadSignal(DownloadBase):
14
16
  def __init__(self, *args, **kwargs):
15
17
  super(DownloadSignal, self).__init__(*args, **kwargs)
16
-
18
+
17
19
  @staticmethod
18
20
  async def get_pack(pack_id: str, pack_key: str) -> StickerPack:
19
21
  async with StickersClient() as client:
20
22
  pack = await client.get_pack(pack_id, pack_key)
21
-
23
+
22
24
  return pack
23
25
 
24
26
  def save_stickers(self, pack: StickerPack):
25
27
  if self.cb_bar:
26
- self.cb_bar(set_progress_mode='determinate', steps=len(pack.stickers))
28
+ self.cb_bar(set_progress_mode="determinate", steps=len(pack.stickers))
27
29
 
28
30
  emoji_dict = {}
29
31
  for sticker in pack.stickers:
30
32
  f_id = str(sticker.id).zfill(3)
31
- f_path = os.path.join(self.out_dir, f'{f_id}')
32
- with open(f_path, "wb",) as f:
33
+ f_path = os.path.join(self.out_dir, f"{f_id}")
34
+ with open(
35
+ f_path,
36
+ "wb",
37
+ ) as f:
33
38
  f.write(sticker.image_data)
34
39
 
35
40
  emoji_dict[f_id] = sticker.emoji
36
41
 
37
42
  codec = CodecInfo.get_file_codec(f_path)
38
- if 'apng' in codec:
39
- f_path_new = f_path + '.apng'
40
- elif 'png' in codec:
41
- f_path_new = f_path + '.png'
42
- elif 'webp' in codec:
43
- f_path_new = f_path + '.webp'
43
+ if "apng" in codec:
44
+ f_path_new = f_path + ".apng"
45
+ elif "png" in codec:
46
+ f_path_new = f_path + ".png"
47
+ elif "webp" in codec:
48
+ f_path_new = f_path + ".webp"
44
49
  else:
45
- self.cb_msg(f'Unknown codec {codec}, defaulting to webp')
46
- codec = 'webp'
47
- f_path_new = f_path + '.webp'
48
-
50
+ self.cb_msg(f"Unknown codec {codec}, defaulting to webp")
51
+ codec = "webp"
52
+ f_path_new = f_path + ".webp"
53
+
49
54
  os.rename(f_path, f_path_new)
50
-
51
- self.cb_msg(f'Downloaded {f_id}.{codec}')
55
+
56
+ self.cb_msg(f"Downloaded {f_id}.{codec}")
52
57
  if self.cb_bar:
53
58
  self.cb_bar(update_bar=True)
54
-
55
- MetadataHandler.set_metadata(self.out_dir, title=pack.title, author=pack.author, emoji_dict=emoji_dict)
59
+
60
+ MetadataHandler.set_metadata(
61
+ self.out_dir, title=pack.title, author=pack.author, emoji_dict=emoji_dict
62
+ )
56
63
 
57
64
  def download_stickers_signal(self) -> bool:
58
- if 'signal.art' not in self.url:
59
- self.cb_msg('Download failed: Unrecognized URL format')
65
+ if "signal.art" not in self.url:
66
+ self.cb_msg("Download failed: Unrecognized URL format")
60
67
  return False
61
68
 
62
- pack_id = self.url.split('#pack_id=')[1].split('&pack_key=')[0]
63
- pack_key = self.url.split('&pack_key=')[1]
69
+ pack_id = self.url.split("#pack_id=")[1].split("&pack_key=")[0]
70
+ pack_key = self.url.split("&pack_key=")[1]
64
71
 
65
72
  pack = anyio.run(DownloadSignal.get_pack, pack_id, pack_key)
66
73
  self.save_stickers(pack)
67
74
 
68
75
  return True
69
-
76
+
70
77
  @staticmethod
71
- def start(url: str, out_dir: str, opt_cred: Optional[dict] = None, cb_msg=print, cb_msg_block=input, cb_bar=None) -> bool:
72
- downloader = DownloadSignal(url, out_dir, opt_cred, cb_msg, cb_msg_block, cb_bar)
73
- return downloader.download_stickers_signal()
78
+ def start(
79
+ url: str,
80
+ out_dir: str,
81
+ opt_cred: Optional[CredOption] = None,
82
+ cb_msg=print,
83
+ cb_msg_block=input,
84
+ cb_bar=None,
85
+ ) -> bool:
86
+ downloader = DownloadSignal(
87
+ url, out_dir, opt_cred, cb_msg, cb_msg_block, cb_bar
88
+ )
89
+ return downloader.download_stickers_signal()