sticker-convert 2.13.3.0__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 -27
- sticker_convert/auth/__init__.py +0 -0
- sticker_convert/auth/auth_base.py +19 -0
- sticker_convert/{utils/auth/get_discord_auth.py → auth/auth_discord.py} +149 -118
- sticker_convert/{utils/auth/get_kakao_auth.py → auth/auth_kakao_android_login.py} +331 -330
- sticker_convert/auth/auth_kakao_desktop_login.py +327 -0
- sticker_convert/{utils/auth/get_kakao_desktop_auth.py → auth/auth_kakao_desktop_memdump.py} +281 -263
- sticker_convert/{utils/auth/get_line_auth.py → auth/auth_line.py} +98 -80
- sticker_convert/{utils/auth/get_signal_auth.py → auth/auth_signal.py} +139 -135
- sticker_convert/auth/auth_telethon.py +161 -0
- sticker_convert/{utils/auth/get_viber_auth.py → auth/auth_viber.py} +250 -235
- sticker_convert/{utils/auth → auth}/telegram_api.py +736 -675
- sticker_convert/cli.py +623 -608
- sticker_convert/converter.py +1093 -1084
- sticker_convert/definitions.py +4 -0
- sticker_convert/downloaders/download_band.py +111 -110
- sticker_convert/downloaders/download_base.py +171 -166
- sticker_convert/downloaders/download_discord.py +92 -91
- sticker_convert/downloaders/download_kakao.py +417 -404
- sticker_convert/downloaders/download_line.py +484 -475
- sticker_convert/downloaders/download_ogq.py +80 -79
- sticker_convert/downloaders/download_signal.py +108 -105
- sticker_convert/downloaders/download_telegram.py +56 -55
- sticker_convert/downloaders/download_viber.py +121 -120
- sticker_convert/gui.py +788 -873
- sticker_convert/gui_components/frames/comp_frame.py +180 -166
- 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 -233
- 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 -757
- sticker_convert/gui_components/windows/base_window.py +7 -2
- sticker_convert/gui_components/windows/discord_get_auth_window.py +79 -82
- sticker_convert/gui_components/windows/kakao_get_auth_window.py +511 -321
- sticker_convert/gui_components/windows/line_get_auth_window.py +94 -102
- sticker_convert/gui_components/windows/signal_get_auth_window.py +84 -89
- sticker_convert/gui_components/windows/viber_get_auth_window.py +168 -168
- sticker_convert/ios-message-stickers-template/.github/FUNDING.yml +3 -3
- 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 2.sticker/Contents.json +9 -9
- 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/iMessage App Icon.stickersiconset/Contents.json +91 -91
- 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/xcuserdata/niklaspeterson.xcuserdatad/xcschemes/xcschememanagement.plist +14 -14
- sticker_convert/job.py +166 -130
- sticker_convert/job_option.py +1 -0
- 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/help.ja_JP.json +88 -0
- sticker_convert/resources/help.json +10 -7
- 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 -121
- sticker_convert/resources/input.zh_CN.json +74 -0
- sticker_convert/resources/input.zh_TW.json +74 -0
- sticker_convert/resources/output.ja_JP.json +38 -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 -177
- sticker_convert/uploaders/upload_base.py +44 -35
- sticker_convert/uploaders/upload_signal.py +218 -203
- sticker_convert/uploaders/upload_telegram.py +353 -338
- sticker_convert/uploaders/upload_viber.py +178 -169
- sticker_convert/uploaders/xcode_imessage.py +295 -286
- sticker_convert/utils/callback.py +238 -6
- sticker_convert/utils/emoji.py +16 -4
- sticker_convert/utils/files/json_resources_loader.py +24 -19
- sticker_convert/utils/files/metadata_handler.py +3 -3
- sticker_convert/utils/translate.py +108 -0
- sticker_convert/utils/url_detect.py +40 -37
- sticker_convert/version.py +1 -1
- {sticker_convert-2.13.3.0.dist-info → sticker_convert-2.17.0.0.dist-info}/METADATA +89 -74
- {sticker_convert-2.13.3.0.dist-info → sticker_convert-2.17.0.0.dist-info}/RECORD +91 -74
- sticker_convert/utils/auth/telethon_setup.py +0 -97
- sticker_convert/utils/singletons.py +0 -18
- {sticker_convert-2.13.3.0.dist-info → sticker_convert-2.17.0.0.dist-info}/WHEEL +0 -0
- {sticker_convert-2.13.3.0.dist-info → sticker_convert-2.17.0.0.dist-info}/entry_points.txt +0 -0
- {sticker_convert-2.13.3.0.dist-info → sticker_convert-2.17.0.0.dist-info}/licenses/LICENSE +0 -0
- {sticker_convert-2.13.3.0.dist-info → sticker_convert-2.17.0.0.dist-info}/top_level.txt +0 -0
|
@@ -1,404 +1,417 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
from __future__ import annotations
|
|
3
|
-
|
|
4
|
-
import itertools
|
|
5
|
-
import json
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from typing import Any, Dict, List, Optional, Tuple
|
|
8
|
-
from urllib.parse import urlparse
|
|
9
|
-
|
|
10
|
-
import requests
|
|
11
|
-
|
|
12
|
-
from sticker_convert.downloaders.download_base import DownloadBase
|
|
13
|
-
from sticker_convert.job_option import CredOption, InputOption
|
|
14
|
-
from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
|
|
15
|
-
from sticker_convert.utils.files.metadata_handler import MetadataHandler
|
|
16
|
-
from sticker_convert.utils.media.decrypt_kakao import DecryptKakao
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
"
|
|
134
|
-
"Talk-
|
|
135
|
-
"
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
self.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
self.
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
self.
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
if
|
|
339
|
-
self.
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
r
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
)
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
if
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import itertools
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
8
|
+
from urllib.parse import urlparse
|
|
9
|
+
|
|
10
|
+
import requests
|
|
11
|
+
|
|
12
|
+
from sticker_convert.downloaders.download_base import DownloadBase
|
|
13
|
+
from sticker_convert.job_option import CredOption, InputOption
|
|
14
|
+
from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
|
|
15
|
+
from sticker_convert.utils.files.metadata_handler import MetadataHandler
|
|
16
|
+
from sticker_convert.utils.media.decrypt_kakao import DecryptKakao
|
|
17
|
+
from sticker_convert.utils.translate import I
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class MetadataKakao:
|
|
21
|
+
@staticmethod
|
|
22
|
+
def share_link_to_public_link(share_link: str) -> str:
|
|
23
|
+
# Share link redirect to preview link if use desktop headers
|
|
24
|
+
headers_desktop = {"User-Agent": "Chrome"}
|
|
25
|
+
r = requests.get(share_link, headers=headers_desktop, allow_redirects=True)
|
|
26
|
+
return r.url
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def get_item_code_from_hash(hash: str, auth_token: str) -> Optional[str]:
|
|
30
|
+
headers = {
|
|
31
|
+
"Authorization": auth_token,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
data = {"hashedItemCode": hash}
|
|
35
|
+
|
|
36
|
+
response = requests.post(
|
|
37
|
+
"https://talk-pilsner.kakao.com/emoticon/api/store/v3/item-code-by-hash",
|
|
38
|
+
headers=headers,
|
|
39
|
+
data=data,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if response.status_code != 200:
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
response_json = json.loads(response.text)
|
|
46
|
+
item_code = response_json["itemCode"]
|
|
47
|
+
|
|
48
|
+
return item_code
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def get_item_code_from_search(
|
|
52
|
+
pack_title: str, search_term: str, by_author: bool, auth_token: str
|
|
53
|
+
) -> str:
|
|
54
|
+
headers = {
|
|
55
|
+
"Authorization": auth_token,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
data = {"query": search_term}
|
|
59
|
+
|
|
60
|
+
response = requests.post(
|
|
61
|
+
"https://talk-pilsner.kakao.com/emoticon/item_store/instant_search",
|
|
62
|
+
headers=headers,
|
|
63
|
+
data=data,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
if response.status_code != 200:
|
|
67
|
+
return "auth_error"
|
|
68
|
+
|
|
69
|
+
def check_pack_match(pack_info: Dict[str, Any]) -> bool:
|
|
70
|
+
share_link = pack_info["itemMetaInfo"]["shareData"]["linkUrl"]
|
|
71
|
+
public_url = MetadataKakao.share_link_to_public_link(share_link)
|
|
72
|
+
if pack_title == urlparse(public_url).path.split("/")[-1]:
|
|
73
|
+
return True
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
response_json = json.loads(response.text)
|
|
77
|
+
for emoticon in response_json["emoticons"]:
|
|
78
|
+
item_code = emoticon["item_code"]
|
|
79
|
+
pack_info = MetadataKakao.get_pack_info_authed(item_code, auth_token)
|
|
80
|
+
if pack_info is None:
|
|
81
|
+
continue
|
|
82
|
+
if check_pack_match(pack_info):
|
|
83
|
+
return item_code
|
|
84
|
+
if by_author:
|
|
85
|
+
cid = pack_info["itemMetaInfo"]["itemMetaData"]["cid"]
|
|
86
|
+
for item_code in MetadataKakao.get_items_by_creator(cid, auth_token):
|
|
87
|
+
pack_info = MetadataKakao.get_pack_info_authed(
|
|
88
|
+
item_code, auth_token
|
|
89
|
+
)
|
|
90
|
+
if pack_info is None:
|
|
91
|
+
continue
|
|
92
|
+
if check_pack_match(pack_info):
|
|
93
|
+
return item_code
|
|
94
|
+
|
|
95
|
+
return "code_not_found"
|
|
96
|
+
|
|
97
|
+
@staticmethod
|
|
98
|
+
def get_items_by_creator(cid: str, auth_token: str) -> List[str]:
|
|
99
|
+
headers = {"Authorization": auth_token}
|
|
100
|
+
|
|
101
|
+
params = {
|
|
102
|
+
"itemSort": "NEW",
|
|
103
|
+
"offset": "0",
|
|
104
|
+
"size": "30",
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
response = requests.get(
|
|
108
|
+
f"https://talk-pilsner.kakao.com/emoticon/api/store/v3/creators/{cid}",
|
|
109
|
+
headers=headers,
|
|
110
|
+
params=params,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
return [i["item_id"] for i in json.loads(response.text)["items"]]
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def get_pack_info_unauthed(
|
|
117
|
+
pack_title: str,
|
|
118
|
+
) -> Optional[dict[str, Any]]:
|
|
119
|
+
pack_meta_r = requests.get(f"https://e.kakao.com/api/v1/items/t/{pack_title}")
|
|
120
|
+
|
|
121
|
+
if pack_meta_r.status_code == 200:
|
|
122
|
+
pack_meta = json.loads(pack_meta_r.text)
|
|
123
|
+
else:
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
return pack_meta
|
|
127
|
+
|
|
128
|
+
@staticmethod
|
|
129
|
+
def get_pack_info_authed(
|
|
130
|
+
item_code: str, auth_token: str
|
|
131
|
+
) -> Optional[dict[str, Any]]:
|
|
132
|
+
headers = {
|
|
133
|
+
"Authorization": auth_token,
|
|
134
|
+
"Talk-Agent": "android/10.8.1",
|
|
135
|
+
"Talk-Language": "en",
|
|
136
|
+
"User-Agent": "okhttp/4.10.0",
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
response = requests.post(
|
|
140
|
+
f"https://talk-pilsner.kakao.com/emoticon/api/store/v3/items/{item_code}",
|
|
141
|
+
headers=headers,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
if response.status_code != 200:
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
response_json = json.loads(response.text)
|
|
148
|
+
|
|
149
|
+
return response_json
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class DownloadKakao(DownloadBase):
|
|
153
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
154
|
+
self.MSG_AUTH_ERROR = I(
|
|
155
|
+
"Warning: Cannot get item code.\n"
|
|
156
|
+
"Is auth_token invalid / expired? Try to regenerate it.\n"
|
|
157
|
+
"Continue to download static stickers instead?"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
self.MSG_CODE_NOT_FOUND = I(
|
|
161
|
+
"Warning: Cannot get item code.\n"
|
|
162
|
+
"Please use share link instead.\n"
|
|
163
|
+
"Continue to download static stickers instead?"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
super().__init__(*args, **kwargs)
|
|
167
|
+
self.pack_title: Optional[str] = None
|
|
168
|
+
self.author: Optional[str] = None
|
|
169
|
+
self.auth_token: Optional[str] = None
|
|
170
|
+
|
|
171
|
+
self.pack_info_unauthed: Optional[dict[str, Any]] = None
|
|
172
|
+
self.pack_info_authed: Optional[dict[str, Any]] = None
|
|
173
|
+
|
|
174
|
+
def download_stickers_kakao(self) -> Tuple[int, int]:
|
|
175
|
+
self.auth_token = None
|
|
176
|
+
if self.opt_cred:
|
|
177
|
+
self.auth_token = self.opt_cred.kakao_auth_token
|
|
178
|
+
|
|
179
|
+
if urlparse(self.url).netloc == "emoticon.kakao.com":
|
|
180
|
+
item_code = None
|
|
181
|
+
if self.auth_token is not None:
|
|
182
|
+
hash = urlparse(self.url).path.split("/")[-1]
|
|
183
|
+
item_code = MetadataKakao.get_item_code_from_hash(hash, self.auth_token)
|
|
184
|
+
|
|
185
|
+
public_url = MetadataKakao.share_link_to_public_link(self.url)
|
|
186
|
+
self.pack_title = urlparse(public_url).path.split("/")[-1]
|
|
187
|
+
pack_info_unauthed = MetadataKakao.get_pack_info_unauthed(self.pack_title)
|
|
188
|
+
if pack_info_unauthed is None:
|
|
189
|
+
self.cb.put(
|
|
190
|
+
I("Download failed: Cannot download metadata for sticker pack")
|
|
191
|
+
)
|
|
192
|
+
return 0, 0
|
|
193
|
+
|
|
194
|
+
self.author = pack_info_unauthed["result"]["artist"]
|
|
195
|
+
thumbnail_urls = pack_info_unauthed["result"]["thumbnailUrls"]
|
|
196
|
+
|
|
197
|
+
if item_code is None:
|
|
198
|
+
if self.auth_token is None:
|
|
199
|
+
self.cb.put(
|
|
200
|
+
I("Warning: Downloading animated sticker requires auth_token")
|
|
201
|
+
)
|
|
202
|
+
else:
|
|
203
|
+
self.cb.put(
|
|
204
|
+
I(
|
|
205
|
+
"Warning: auth_token invalid, cannot download animated sticker"
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
self.cb.put(I("Downloading static stickers..."))
|
|
209
|
+
return self.download_static(thumbnail_urls)
|
|
210
|
+
else:
|
|
211
|
+
return self.download_animated(item_code)
|
|
212
|
+
|
|
213
|
+
if self.url.isnumeric():
|
|
214
|
+
self.pack_title = None
|
|
215
|
+
if self.auth_token:
|
|
216
|
+
self.pack_info_authed = MetadataKakao.get_pack_info_authed(
|
|
217
|
+
self.url, self.auth_token
|
|
218
|
+
)
|
|
219
|
+
if self.pack_info_authed:
|
|
220
|
+
self.pack_title = self.pack_info_authed["itemUnitInfo"][0]["title"]
|
|
221
|
+
else:
|
|
222
|
+
self.cb.put(I("Warning: Cannot get pack_title with auth_token."))
|
|
223
|
+
self.cb.put(
|
|
224
|
+
I("Is auth_token invalid / expired? Try to regenerate it.")
|
|
225
|
+
)
|
|
226
|
+
self.cb.put(I("Continuing without getting pack_title"))
|
|
227
|
+
|
|
228
|
+
return self.download_animated(self.url)
|
|
229
|
+
|
|
230
|
+
if urlparse(self.url).netloc == "e.kakao.com":
|
|
231
|
+
self.pack_title = urlparse(self.url).path.split("/")[-1]
|
|
232
|
+
self.pack_info_unauthed = MetadataKakao.get_pack_info_unauthed(
|
|
233
|
+
self.pack_title
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
if not self.pack_info_unauthed:
|
|
237
|
+
self.cb.put(
|
|
238
|
+
I("Download failed: Cannot download metadata for sticker pack")
|
|
239
|
+
)
|
|
240
|
+
return 0, 0
|
|
241
|
+
|
|
242
|
+
self.author = self.pack_info_unauthed["result"]["artist"]
|
|
243
|
+
title_ko = self.pack_info_unauthed["result"]["title"]
|
|
244
|
+
thumbnail_urls = self.pack_info_unauthed["result"]["thumbnailUrls"]
|
|
245
|
+
|
|
246
|
+
if self.auth_token:
|
|
247
|
+
item_code = MetadataKakao.get_item_code_from_search(
|
|
248
|
+
self.pack_title, title_ko, False, self.auth_token
|
|
249
|
+
)
|
|
250
|
+
if item_code == "auth_error":
|
|
251
|
+
msg = self.MSG_AUTH_ERROR
|
|
252
|
+
elif item_code == "code_not_found":
|
|
253
|
+
self.cb.put(
|
|
254
|
+
I(
|
|
255
|
+
"Cannot get item code, trying to search by author name, this may take long time..."
|
|
256
|
+
)
|
|
257
|
+
)
|
|
258
|
+
self.cb.put(
|
|
259
|
+
I("Hint: Use share link instead to download more reliably")
|
|
260
|
+
)
|
|
261
|
+
if self.author is not None:
|
|
262
|
+
item_code = MetadataKakao.get_item_code_from_search(
|
|
263
|
+
self.pack_title, self.author, True, self.auth_token
|
|
264
|
+
)
|
|
265
|
+
if item_code == "code_not_found":
|
|
266
|
+
msg = self.MSG_CODE_NOT_FOUND
|
|
267
|
+
else:
|
|
268
|
+
return self.download_animated(item_code)
|
|
269
|
+
else:
|
|
270
|
+
return self.download_animated(item_code)
|
|
271
|
+
|
|
272
|
+
self.cb.put(("ask_bool", (msg,), None))
|
|
273
|
+
if self.cb_return:
|
|
274
|
+
response = self.cb_return.get_response()
|
|
275
|
+
else:
|
|
276
|
+
response = False
|
|
277
|
+
|
|
278
|
+
if response is False:
|
|
279
|
+
return 0, 0
|
|
280
|
+
|
|
281
|
+
return self.download_static(thumbnail_urls)
|
|
282
|
+
|
|
283
|
+
self.cb.put(I("Download failed: Unrecognized URL"))
|
|
284
|
+
return 0, 0
|
|
285
|
+
|
|
286
|
+
def download_static(self, thumbnail_urls: str) -> Tuple[int, int]:
|
|
287
|
+
headers = {"User-Agent": "Android"}
|
|
288
|
+
MetadataHandler.set_metadata(
|
|
289
|
+
self.out_dir, title=self.pack_title, author=self.author
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
targets: List[Tuple[str, Path]] = []
|
|
293
|
+
|
|
294
|
+
for num, url in enumerate(thumbnail_urls):
|
|
295
|
+
dest = Path(self.out_dir, str(num).zfill(3) + ".png")
|
|
296
|
+
targets.append((url, dest))
|
|
297
|
+
|
|
298
|
+
results = self.download_multiple_files(targets, headers=headers)
|
|
299
|
+
|
|
300
|
+
return sum(results.values()), len(targets)
|
|
301
|
+
|
|
302
|
+
def download_animated(self, item_code: str) -> Tuple[int, int]:
|
|
303
|
+
MetadataHandler.set_metadata(
|
|
304
|
+
self.out_dir, title=self.pack_title, author=self.author
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
play_exts = [".webp", ".gif", ".png", ""]
|
|
308
|
+
play_types = ["emot", "emoji", ""] # emot = normal; emoji = mini
|
|
309
|
+
play_path_format = None
|
|
310
|
+
sound_exts = [".mp3", ""]
|
|
311
|
+
sound_path_format = None
|
|
312
|
+
stickers_count = 32 # https://emoticonstudio.kakao.com/pages/start
|
|
313
|
+
|
|
314
|
+
headers = {"User-Agent": "Android"}
|
|
315
|
+
|
|
316
|
+
if not self.pack_info_authed and self.auth_token:
|
|
317
|
+
self.pack_info_authed = MetadataKakao.get_pack_info_authed(
|
|
318
|
+
item_code, self.auth_token
|
|
319
|
+
)
|
|
320
|
+
if self.pack_info_authed:
|
|
321
|
+
preview_data = self.pack_info_authed["itemUnitInfo"][0]["previewData"]
|
|
322
|
+
play_path_format = preview_data["playPathFormat"]
|
|
323
|
+
sound_path_format = preview_data["soundPathFormat"]
|
|
324
|
+
stickers_count = preview_data["num"]
|
|
325
|
+
else:
|
|
326
|
+
if not self.pack_info_unauthed:
|
|
327
|
+
public_url = None
|
|
328
|
+
if urlparse(self.url).netloc == "emoticon.kakao.com":
|
|
329
|
+
public_url = MetadataKakao.share_link_to_public_link(self.url)
|
|
330
|
+
elif urlparse(self.url).netloc == "e.kakao.com":
|
|
331
|
+
public_url = self.url
|
|
332
|
+
if public_url:
|
|
333
|
+
pack_title = urlparse(public_url).path.split("/")[-1]
|
|
334
|
+
self.pack_info_unauthed = MetadataKakao.get_pack_info_unauthed(
|
|
335
|
+
pack_title
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
if self.pack_info_unauthed:
|
|
339
|
+
stickers_count = len(self.pack_info_unauthed["result"]["thumbnailUrls"])
|
|
340
|
+
|
|
341
|
+
play_type = ""
|
|
342
|
+
play_ext = ""
|
|
343
|
+
if play_path_format is None:
|
|
344
|
+
for play_type, play_ext in itertools.product(play_types, play_exts):
|
|
345
|
+
r = requests.get(
|
|
346
|
+
f"https://item.kakaocdn.net/dw/{item_code}.{play_type}_001{play_ext}",
|
|
347
|
+
headers=headers,
|
|
348
|
+
)
|
|
349
|
+
if r.ok:
|
|
350
|
+
break
|
|
351
|
+
if play_ext == "":
|
|
352
|
+
self.cb.put(I("Failed to determine extension of {}").format(item_code))
|
|
353
|
+
return 0, 0
|
|
354
|
+
else:
|
|
355
|
+
play_path_format = f"dw/{item_code}.{play_type}_0##{play_ext}"
|
|
356
|
+
else:
|
|
357
|
+
play_ext = "." + play_path_format.split(".")[-1]
|
|
358
|
+
|
|
359
|
+
sound_ext = ""
|
|
360
|
+
if sound_path_format is None:
|
|
361
|
+
for sound_ext in sound_exts:
|
|
362
|
+
r = requests.get(
|
|
363
|
+
f"https://item.kakaocdn.net/dw/{item_code}.sound_001{sound_ext}",
|
|
364
|
+
headers=headers,
|
|
365
|
+
)
|
|
366
|
+
if r.ok:
|
|
367
|
+
break
|
|
368
|
+
if sound_ext != "":
|
|
369
|
+
sound_path_format = f"dw/{item_code}.sound_0##{sound_ext}"
|
|
370
|
+
elif sound_path_format != "":
|
|
371
|
+
sound_ext = "." + sound_path_format.split(".")[-1]
|
|
372
|
+
|
|
373
|
+
assert play_path_format
|
|
374
|
+
targets: list[tuple[str, Path]] = []
|
|
375
|
+
for num in range(1, stickers_count + 1):
|
|
376
|
+
play_url = "https://item.kakaocdn.net/" + play_path_format.replace(
|
|
377
|
+
"##", str(num).zfill(2)
|
|
378
|
+
)
|
|
379
|
+
play_dl_path = Path(self.out_dir, str(num).zfill(3) + play_ext)
|
|
380
|
+
targets.append((play_url, play_dl_path))
|
|
381
|
+
|
|
382
|
+
if sound_path_format:
|
|
383
|
+
sound_url = "https://item.kakaocdn.net/" + sound_path_format.replace(
|
|
384
|
+
"##", str(num).zfill(2)
|
|
385
|
+
)
|
|
386
|
+
sound_dl_path = Path(self.out_dir, str(num).zfill(3) + sound_ext)
|
|
387
|
+
targets.append((sound_url, sound_dl_path))
|
|
388
|
+
|
|
389
|
+
results = self.download_multiple_files(targets, headers=headers)
|
|
390
|
+
|
|
391
|
+
for target in targets:
|
|
392
|
+
f_path = target[1]
|
|
393
|
+
ext = Path(f_path).suffix
|
|
394
|
+
|
|
395
|
+
if ext not in (".gif", ".webp"):
|
|
396
|
+
continue
|
|
397
|
+
|
|
398
|
+
with open(f_path, "rb") as f:
|
|
399
|
+
data = f.read()
|
|
400
|
+
data = DecryptKakao.xor_data(data)
|
|
401
|
+
self.cb.put(I("Decrypted {}").format(f_path))
|
|
402
|
+
with open(f_path, "wb+") as f:
|
|
403
|
+
f.write(data)
|
|
404
|
+
|
|
405
|
+
self.cb.put(I("Finished getting {}").format(item_code))
|
|
406
|
+
|
|
407
|
+
return sum(results.values()), len(targets)
|
|
408
|
+
|
|
409
|
+
@staticmethod
|
|
410
|
+
def start(
|
|
411
|
+
opt_input: InputOption,
|
|
412
|
+
opt_cred: Optional[CredOption],
|
|
413
|
+
cb: CallbackProtocol,
|
|
414
|
+
cb_return: CallbackReturn,
|
|
415
|
+
) -> Tuple[int, int]:
|
|
416
|
+
downloader = DownloadKakao(opt_input, opt_cred, cb, cb_return)
|
|
417
|
+
return downloader.download_stickers_kakao()
|