sticker-convert 2.11.8__py3-none-any.whl → 2.12.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/converter.py +72 -0
- sticker_convert/definitions.py +7 -0
- sticker_convert/downloaders/download_kakao.py +63 -78
- sticker_convert/downloaders/download_viber.py +20 -6
- sticker_convert/ios-message-stickers-template/.github/FUNDING.yml +0 -0
- sticker_convert/ios-message-stickers-template/.gitignore +0 -0
- sticker_convert/ios-message-stickers-template/README.md +0 -0
- sticker_convert/ios-message-stickers-template/stickers/Info.plist +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Info.plist +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Contents.json +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Contents.json +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 1.sticker/Contents.json +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 1.sticker/Sticker 1.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 2.sticker/Contents.json +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 2.sticker/Sticker 2.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 3.sticker/Contents.json +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 3.sticker/Sticker 3.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/App-Store-1024x1024pt.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Contents.json +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-App-Store-1024x768pt.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPad-67x50pt@2x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPad-Pro-74x55pt@2x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPhone-60x45pt@2x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages-iPhone-60x45pt@3x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages27x20pt@2x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages27x20pt@3x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages32x24pt@2x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Messages32x24pt@3x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPad-Settings-29pt@2x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPhone-Settings-29pt@3x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/iPhone-settings-29pt@2x.png +0 -0
- sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.pbxproj +0 -0
- sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
- sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -0
- sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/xcuserdata/niklaspeterson.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- sticker_convert/ios-message-stickers-template/stickers.xcodeproj/xcuserdata/niklaspeterson.xcuserdatad/xcschemes/xcschememanagement.plist +0 -0
- sticker_convert/resources/emoji.json +0 -0
- sticker_convert/resources/help.json +0 -0
- sticker_convert/resources/input.json +1 -1
- sticker_convert/resources/memdump_windows.ps1 +0 -0
- sticker_convert/resources/output.json +0 -0
- sticker_convert/uploaders/upload_viber.py +13 -14
- sticker_convert/utils/auth/get_kakao_auth.py +34 -4
- sticker_convert/utils/auth/get_kakao_desktop_auth.py +1 -1
- sticker_convert/utils/chrome_remotedebug.py +78 -15
- sticker_convert/utils/files/metadata_handler.py +1 -0
- sticker_convert/utils/media/codec_info.py +53 -6
- sticker_convert/utils/url_detect.py +1 -3
- sticker_convert/version.py +1 -1
- {sticker_convert-2.11.8.dist-info → sticker_convert-2.12.0.dist-info}/METADATA +4 -5
- {sticker_convert-2.11.8.dist-info → sticker_convert-2.12.0.dist-info}/RECORD +19 -19
- {sticker_convert-2.11.8.dist-info → sticker_convert-2.12.0.dist-info}/WHEEL +0 -0
- {sticker_convert-2.11.8.dist-info → sticker_convert-2.12.0.dist-info}/entry_points.txt +0 -0
- {sticker_convert-2.11.8.dist-info → sticker_convert-2.12.0.dist-info}/licenses/LICENSE +0 -0
- {sticker_convert-2.11.8.dist-info → sticker_convert-2.12.0.dist-info}/top_level.txt +0 -0
sticker_convert/converter.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
|
+
import json
|
2
3
|
import os
|
3
4
|
from fractions import Fraction
|
4
5
|
from io import BytesIO
|
@@ -7,12 +8,14 @@ from pathlib import Path
|
|
7
8
|
from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Union, cast
|
8
9
|
|
9
10
|
import numpy as np
|
11
|
+
from bs4 import BeautifulSoup
|
10
12
|
from PIL import Image
|
11
13
|
from PIL import __version__ as PillowVersion
|
12
14
|
from PIL import features
|
13
15
|
|
14
16
|
from sticker_convert.job_option import CompOption
|
15
17
|
from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
|
18
|
+
from sticker_convert.utils.chrome_remotedebug import CRD
|
16
19
|
from sticker_convert.utils.files.cache_store import CacheStore
|
17
20
|
from sticker_convert.utils.media.codec_info import CodecInfo, rounding
|
18
21
|
from sticker_convert.utils.media.format_verify import FormatVerify
|
@@ -423,9 +426,78 @@ class StickerConvert:
|
|
423
426
|
# ffmpeg do not support webp decoding (yet)
|
424
427
|
# ffmpeg could fail to decode apng if file is buggy
|
425
428
|
self._frames_import_pillow()
|
429
|
+
elif suffix == ".svg":
|
430
|
+
self._frames_import_svg()
|
426
431
|
else:
|
427
432
|
self._frames_import_pyav()
|
428
433
|
|
434
|
+
def _frames_import_svg(self) -> None:
|
435
|
+
width = self.codec_info_orig.res[0]
|
436
|
+
height = self.codec_info_orig.res[1]
|
437
|
+
|
438
|
+
chrome_path = CRD.get_chrome_path()
|
439
|
+
args = [
|
440
|
+
"--headless",
|
441
|
+
"--disable-extensions",
|
442
|
+
"--disable-infobars",
|
443
|
+
"--disable-gpu",
|
444
|
+
"--disable-gpu-rasterization",
|
445
|
+
"--hide-scrollbars",
|
446
|
+
f"--window-size={width + 100},{height + 100}",
|
447
|
+
"about:blank",
|
448
|
+
]
|
449
|
+
if chrome_path is None:
|
450
|
+
raise RuntimeError("[F] Chrome/Chromium required for importing svg")
|
451
|
+
self.cb.put("[W] Importing SVG takes long time")
|
452
|
+
|
453
|
+
if isinstance(self.in_f, bytes):
|
454
|
+
svg = self.in_f.decode()
|
455
|
+
else:
|
456
|
+
with open(self.in_f) as f:
|
457
|
+
svg = f.read()
|
458
|
+
soup = BeautifulSoup(svg, "html.parser")
|
459
|
+
svg_tag = soup.find_all("svg")[0]
|
460
|
+
|
461
|
+
if svg_tag.get("width") is None:
|
462
|
+
svg_tag["width"] = width
|
463
|
+
if svg_tag.get("height") is None:
|
464
|
+
svg_tag["height"] = height
|
465
|
+
svg = str(soup)
|
466
|
+
|
467
|
+
crd = None
|
468
|
+
try:
|
469
|
+
crd = CRD(chrome_path, args=args)
|
470
|
+
crd.connect(-1)
|
471
|
+
crd.open_html_str(svg)
|
472
|
+
crd.set_transparent_bg()
|
473
|
+
crd.exec_js('svg = document.getElementsByTagName("svg")[0]')
|
474
|
+
x = json.loads(crd.exec_js("svg.getBoundingClientRect().x"))["result"][
|
475
|
+
"result"
|
476
|
+
]["value"]
|
477
|
+
y = json.loads(crd.exec_js("svg.getBoundingClientRect().y"))["result"][
|
478
|
+
"result"
|
479
|
+
]["value"]
|
480
|
+
clip = {"x": x, "y": y, "width": width, "height": height, "scale": 1}
|
481
|
+
|
482
|
+
if self.codec_info_orig.fps > 0:
|
483
|
+
crd.exec_js("svg.pauseAnimations()")
|
484
|
+
for i in range(self.codec_info_orig.frames):
|
485
|
+
curr_time = (
|
486
|
+
i
|
487
|
+
/ self.codec_info_orig.frames
|
488
|
+
* self.codec_info_orig.duration
|
489
|
+
/ 1000
|
490
|
+
)
|
491
|
+
crd.exec_js(f"svg.setCurrentTime({curr_time})")
|
492
|
+
self.frames_raw.append(
|
493
|
+
np.asarray(crd.screenshot(clip).convert("RGBA"))
|
494
|
+
)
|
495
|
+
else:
|
496
|
+
self.frames_raw.append(np.asarray(crd.screenshot(clip).convert("RGBA")))
|
497
|
+
finally:
|
498
|
+
if crd is not None:
|
499
|
+
crd.close()
|
500
|
+
|
429
501
|
def _frames_import_pillow(self) -> None:
|
430
502
|
with Image.open(self.in_f) as im:
|
431
503
|
# Note: im.convert("RGBA") would return rgba image of current frame only
|
sticker_convert/definitions.py
CHANGED
@@ -82,3 +82,10 @@ def get_config_dir() -> Path:
|
|
82
82
|
|
83
83
|
# Directory for saving configs
|
84
84
|
CONFIG_DIR = get_config_dir()
|
85
|
+
|
86
|
+
# When importing SVG, import at this fps
|
87
|
+
SVG_SAMPLE_FPS = 30
|
88
|
+
|
89
|
+
# If width and height not set in SVG tag, import at this dimension
|
90
|
+
SVG_DEFAULT_WIDTH = 1024
|
91
|
+
SVG_DEFAULT_HEIGHT = 1024
|
@@ -4,12 +4,10 @@ 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, List, Optional, Tuple
|
8
8
|
from urllib.parse import urlparse
|
9
9
|
|
10
10
|
import requests
|
11
|
-
from bs4 import BeautifulSoup
|
12
|
-
from py_mini_racer import MiniRacer
|
13
11
|
|
14
12
|
from sticker_convert.downloaders.download_base import DownloadBase
|
15
13
|
from sticker_convert.job_option import CredOption, InputOption
|
@@ -17,34 +15,32 @@ from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
|
|
17
15
|
from sticker_convert.utils.files.metadata_handler import MetadataHandler
|
18
16
|
from sticker_convert.utils.media.decrypt_kakao import DecryptKakao
|
19
17
|
|
20
|
-
JSINJECT = """
|
21
|
-
class osclass {
|
22
|
-
android = true;
|
23
|
-
}
|
24
|
-
class uaclass {
|
25
|
-
os = new osclass();
|
26
|
-
}
|
27
|
-
class util {
|
28
|
-
static userAgent() {
|
29
|
-
return new uaclass();
|
30
|
-
}
|
31
|
-
}
|
32
|
-
class daumtools {
|
33
|
-
static web2app(dataDict) {
|
34
|
-
return dataDict['urlScheme'];
|
35
|
-
}
|
36
|
-
}
|
37
|
-
class document {
|
38
|
-
static querySelectorAll(selectors) {
|
39
|
-
return [];
|
40
|
-
}
|
41
|
-
}
|
42
|
-
"""
|
43
|
-
|
44
18
|
|
45
19
|
class MetadataKakao:
|
46
20
|
@staticmethod
|
47
|
-
def
|
21
|
+
def get_item_code_from_hash(hash: str, auth_token: str) -> Optional[str]:
|
22
|
+
headers = {
|
23
|
+
"Authorization": auth_token,
|
24
|
+
}
|
25
|
+
|
26
|
+
data = {"hashedItemCode": hash}
|
27
|
+
|
28
|
+
response = requests.post(
|
29
|
+
"https://talk-pilsner.kakao.com/emoticon/api/store/v3/item-code-by-hash",
|
30
|
+
headers=headers,
|
31
|
+
data=data,
|
32
|
+
)
|
33
|
+
|
34
|
+
if response.status_code != 200:
|
35
|
+
return None
|
36
|
+
|
37
|
+
response_json = json.loads(response.text)
|
38
|
+
item_code = response_json["itemCode"]
|
39
|
+
|
40
|
+
return item_code
|
41
|
+
|
42
|
+
@staticmethod
|
43
|
+
def get_item_code_from_title(title_ko: str, auth_token: str) -> Optional[str]:
|
48
44
|
headers = {
|
49
45
|
"Authorization": auth_token,
|
50
46
|
}
|
@@ -112,66 +108,53 @@ class DownloadKakao(DownloadBase):
|
|
112
108
|
self.pack_info_unauthed: Optional[dict[str, Any]] = None
|
113
109
|
self.pack_info_authed: Optional[dict[str, Any]] = None
|
114
110
|
|
115
|
-
def get_info_from_share_link(self, url: str) -> Tuple[Optional[str], Optional[str]]:
|
116
|
-
headers = {"User-Agent": "Android"}
|
117
|
-
|
118
|
-
response = requests.get(url, headers=headers)
|
119
|
-
soup = BeautifulSoup(response.content.decode("utf-8", "ignore"), "html.parser")
|
120
|
-
|
121
|
-
pack_title_tag = soup.find("title") # type: ignore
|
122
|
-
if not pack_title_tag:
|
123
|
-
return None, None
|
124
|
-
|
125
|
-
pack_title: str = pack_title_tag.string # type: ignore
|
126
|
-
|
127
|
-
js = ""
|
128
|
-
for script_tag in soup.find_all("script"):
|
129
|
-
js = script_tag.string
|
130
|
-
if js and "daumtools.web2app" in js:
|
131
|
-
break
|
132
|
-
if "daumtools.web2app" not in js:
|
133
|
-
return None, None
|
134
|
-
|
135
|
-
js = JSINJECT + js
|
136
|
-
|
137
|
-
ctx = MiniRacer()
|
138
|
-
kakao_url = cast(str, ctx.eval(js))
|
139
|
-
item_code = urlparse(kakao_url).path.split("/")[2]
|
140
|
-
|
141
|
-
# Share link redirect to preview link if use desktop headers
|
142
|
-
# This allows us to find pack author
|
143
|
-
headers_desktop = {"User-Agent": "Chrome"}
|
144
|
-
|
145
|
-
response = requests.get(url, headers=headers_desktop, allow_redirects=True)
|
146
|
-
response.url
|
147
|
-
|
148
|
-
pack_title_url = urlparse(response.url).path.split("/")[-1]
|
149
|
-
pack_info_unauthed = MetadataKakao.get_pack_info_unauthed(pack_title_url)
|
150
|
-
if pack_info_unauthed:
|
151
|
-
self.author = pack_info_unauthed["result"]["artist"]
|
152
|
-
|
153
|
-
return pack_title, item_code
|
154
|
-
|
155
111
|
def download_stickers_kakao(self) -> Tuple[int, int]:
|
156
112
|
self.auth_token = None
|
157
113
|
if self.opt_cred:
|
158
114
|
self.auth_token = self.opt_cred.kakao_auth_token
|
159
115
|
|
160
116
|
if urlparse(self.url).netloc == "emoticon.kakao.com":
|
161
|
-
|
117
|
+
item_code = None
|
118
|
+
if self.auth_token is not None:
|
119
|
+
hash = urlparse(self.url).path.split("/")[-1]
|
120
|
+
item_code = MetadataKakao.get_item_code_from_hash(hash, self.auth_token)
|
162
121
|
|
163
|
-
if
|
164
|
-
|
165
|
-
|
166
|
-
|
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]
|
129
|
+
pack_info_unauthed = MetadataKakao.get_pack_info_unauthed(self.pack_title)
|
130
|
+
if pack_info_unauthed is None:
|
131
|
+
self.cb.put(
|
132
|
+
"Download failed: Cannot download metadata for sticker pack"
|
133
|
+
)
|
134
|
+
return 0, 0
|
135
|
+
|
136
|
+
self.author = pack_info_unauthed["result"]["artist"]
|
137
|
+
thumbnail_urls = pack_info_unauthed["result"]["thumbnailUrls"]
|
167
138
|
|
168
|
-
|
169
|
-
|
139
|
+
if item_code is None:
|
140
|
+
if self.auth_token is None:
|
141
|
+
self.cb.put(
|
142
|
+
"Warning: Downloading animated sticker requires auth_token"
|
143
|
+
)
|
144
|
+
else:
|
145
|
+
self.cb.put(
|
146
|
+
"Warning: auth_token invalid, cannot download animated sticker"
|
147
|
+
)
|
148
|
+
self.cb.put("Downloading static stickers...")
|
149
|
+
self.download_static(thumbnail_urls)
|
150
|
+
else:
|
151
|
+
return self.download_animated(item_code)
|
170
152
|
|
153
|
+
if self.url.isnumeric():
|
171
154
|
self.pack_title = None
|
172
155
|
if self.auth_token:
|
173
156
|
self.pack_info_authed = MetadataKakao.get_pack_info_authed(
|
174
|
-
|
157
|
+
self.url, self.auth_token
|
175
158
|
)
|
176
159
|
if self.pack_info_authed:
|
177
160
|
self.pack_title = self.pack_info_authed["itemUnitInfo"][0]["title"]
|
@@ -182,7 +165,7 @@ class DownloadKakao(DownloadBase):
|
|
182
165
|
)
|
183
166
|
self.cb.put("Continuing without getting pack_title")
|
184
167
|
|
185
|
-
return self.download_animated(
|
168
|
+
return self.download_animated(self.url)
|
186
169
|
|
187
170
|
if urlparse(self.url).netloc == "e.kakao.com":
|
188
171
|
self.pack_title = urlparse(self.url).path.split("/")[-1]
|
@@ -201,7 +184,9 @@ class DownloadKakao(DownloadBase):
|
|
201
184
|
thumbnail_urls = self.pack_info_unauthed["result"]["thumbnailUrls"]
|
202
185
|
|
203
186
|
if self.auth_token:
|
204
|
-
item_code = MetadataKakao.
|
187
|
+
item_code = MetadataKakao.get_item_code_from_title(
|
188
|
+
title_ko, self.auth_token
|
189
|
+
)
|
205
190
|
if item_code:
|
206
191
|
return self.download_animated(item_code)
|
207
192
|
msg = "Warning: Cannot get item code.\n"
|
@@ -19,7 +19,7 @@ class DownloadViber(DownloadBase):
|
|
19
19
|
# def __init__(self, *args: Any, **kwargs: Any) -> None:
|
20
20
|
# super().__init__(*args, **kwargs)
|
21
21
|
|
22
|
-
def get_pack_info(self, url: str) -> Optional[Tuple[str, str]]:
|
22
|
+
def get_pack_info(self, url: str) -> Optional[Tuple[str, str, str]]:
|
23
23
|
r = requests.get(url, allow_redirects=True)
|
24
24
|
soup = BeautifulSoup(r.text, "html.parser")
|
25
25
|
|
@@ -41,10 +41,13 @@ class DownloadViber(DownloadBase):
|
|
41
41
|
title = pack_dict["title"]
|
42
42
|
first_sticker_url = cast(str, pack_dict["stickerFirstItemUrl"])
|
43
43
|
zip_url = "/".join(first_sticker_url.split("/")[:-1]) + ".zip"
|
44
|
+
pack_id = pack_dict["id"].split(".")[-1]
|
44
45
|
|
45
|
-
return title, zip_url
|
46
|
+
return title, zip_url, pack_id
|
46
47
|
|
47
|
-
def decompress(
|
48
|
+
def decompress(
|
49
|
+
self, zip_file: bytes, exts: Optional[Tuple[str, ...]] = None
|
50
|
+
) -> int:
|
48
51
|
with zipfile.ZipFile(BytesIO(zip_file)) as zf:
|
49
52
|
self.cb.put("Unzipping...")
|
50
53
|
|
@@ -58,9 +61,14 @@ class DownloadViber(DownloadBase):
|
|
58
61
|
)
|
59
62
|
|
60
63
|
for sticker in zf_files:
|
64
|
+
ext = Path(sticker).suffix
|
65
|
+
if "frame" in sticker or ".db" in sticker or sticker.endswith("/"):
|
66
|
+
continue
|
67
|
+
if exts is not None and ext not in exts:
|
68
|
+
continue
|
61
69
|
num = sticker.split(".")[0][-2:].zfill(3)
|
62
70
|
data = zf.read(sticker)
|
63
|
-
|
71
|
+
|
64
72
|
self.cb.put(f"Read {sticker}")
|
65
73
|
|
66
74
|
out_path = Path(self.out_dir, num + ext)
|
@@ -76,10 +84,16 @@ class DownloadViber(DownloadBase):
|
|
76
84
|
if pack_info is None:
|
77
85
|
self.cb.put("Download failed: Cannot get pack info")
|
78
86
|
return 0, 0
|
79
|
-
title, zip_url = pack_info
|
87
|
+
title, zip_url, pack_id = pack_info
|
80
88
|
|
89
|
+
anim_url = f"https://content.cdn.viber.com/stickers/ASVG/{pack_id}.zip"
|
90
|
+
anim_file = self.download_file(anim_url)
|
81
91
|
zip_file = self.download_file(zip_url)
|
82
|
-
|
92
|
+
if anim_file:
|
93
|
+
count = self.decompress(anim_file, (".svg",))
|
94
|
+
count += self.decompress(zip_file, (".mp3",))
|
95
|
+
else:
|
96
|
+
count = self.decompress(zip_file, (".mp3", ".png"))
|
83
97
|
|
84
98
|
MetadataHandler.set_metadata(self.out_dir, title=title)
|
85
99
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -52,7 +52,7 @@
|
|
52
52
|
"kakao": {
|
53
53
|
"full_name": "Download from Kakao",
|
54
54
|
"help": "Download kakao stickers from a URL / ID as input",
|
55
|
-
"example": "Example: https://e.kakao.com/t/xxxxx
|
55
|
+
"example": "Example: https://e.kakao.com/t/xxxxx \nOR https://emoticon.kakao.com/items/xxxxx OR 4404400",
|
56
56
|
"address_lbls": "URL address / ID",
|
57
57
|
"metadata_provides": {
|
58
58
|
"title": true,
|
File without changes
|
File without changes
|
@@ -72,23 +72,22 @@ class UploadViber(UploadBase):
|
|
72
72
|
separate_image_anim=False,
|
73
73
|
)
|
74
74
|
|
75
|
-
|
76
|
-
if
|
77
|
-
cover_path =
|
75
|
+
cover_path = MetadataHandler.get_cover(self.opt_output.dir)
|
76
|
+
if cover_path is None:
|
77
|
+
cover_path = MetadataHandler.get_stickers_present(self.opt_output.dir)[0]
|
78
|
+
|
79
|
+
if FormatVerify.check_file(cover_path, spec=self.png_cover_spec):
|
80
|
+
with open(cover_path, "rb") as f:
|
81
|
+
cover_bytes = f.read()
|
78
82
|
else:
|
79
|
-
|
80
|
-
0
|
81
|
-
]
|
82
|
-
cover_path = self.opt_output.dir / "cover.png"
|
83
|
-
|
84
|
-
if not FormatVerify.check_file(cover_path_old, spec=self.png_cover_spec):
|
85
|
-
StickerConvert.convert(
|
86
|
-
cover_path_old,
|
83
|
+
_, _, cover_bytes, _ = StickerConvert.convert( # type: ignore
|
87
84
|
cover_path,
|
85
|
+
Path("bytes.png"),
|
88
86
|
self.opt_comp_merged,
|
89
87
|
self.cb,
|
90
88
|
self.cb_return,
|
91
89
|
)
|
90
|
+
assert isinstance(cover_bytes, bytes)
|
92
91
|
|
93
92
|
stickers_total = 0
|
94
93
|
stickers_ok = 0
|
@@ -126,12 +125,12 @@ class UploadViber(UploadBase):
|
|
126
125
|
upload_data["description"] = author
|
127
126
|
upload_data["shareable"] = "1"
|
128
127
|
|
129
|
-
with open(out_f, "rb") as f
|
128
|
+
with open(out_f, "rb") as f:
|
130
129
|
r = requests.post(
|
131
130
|
"https://market.api.viber.com/2/users/custom-sticker-packs/create",
|
132
131
|
files={
|
133
|
-
"file": ("upload.zip", f),
|
134
|
-
"file_icon": ("color_icon.png",
|
132
|
+
"file": ("upload.zip", f.read()),
|
133
|
+
"file_icon": ("color_icon.png", cover_bytes),
|
135
134
|
},
|
136
135
|
data=upload_data,
|
137
136
|
)
|
@@ -6,6 +6,7 @@ from typing import Any, Callable, Dict, Optional
|
|
6
6
|
from urllib.parse import parse_qs, urlparse
|
7
7
|
|
8
8
|
import requests
|
9
|
+
from bs4 import BeautifulSoup
|
9
10
|
|
10
11
|
from sticker_convert.job_option import CredOption
|
11
12
|
|
@@ -32,10 +33,10 @@ class GetKakaoAuth:
|
|
32
33
|
self.uuid_c = str(uuid.uuid4())
|
33
34
|
self.device_info = (
|
34
35
|
f"android/30; uuid={self.device_uuid}; ssaid={self.device_ssaid}; "
|
35
|
-
+
|
36
|
+
+ "model=SDK_GPHONE_X86_64; screen_resolution=1080x1920; sim=310260/1/us; onestore=false; uvc3=null"
|
36
37
|
)
|
37
38
|
self.app_platform = "android"
|
38
|
-
self.app_version_number =
|
39
|
+
self.app_version_number = self.get_version()
|
39
40
|
self.app_language = "en"
|
40
41
|
self.app_version = (
|
41
42
|
f"{self.app_platform}/{self.app_version_number}/{self.app_language}"
|
@@ -44,7 +45,7 @@ class GetKakaoAuth:
|
|
44
45
|
self.headers = {
|
45
46
|
"Host": "katalk.kakao.com",
|
46
47
|
"Accept-Language": "en",
|
47
|
-
"User-Agent": "KT/
|
48
|
+
"User-Agent": f"KT/{self.app_version_number} An/11 en",
|
48
49
|
"Device-Info": self.device_info,
|
49
50
|
"A": self.app_version,
|
50
51
|
"C": self.uuid_c,
|
@@ -52,6 +53,20 @@ class GetKakaoAuth:
|
|
52
53
|
"Connection": "close",
|
53
54
|
}
|
54
55
|
|
56
|
+
def get_version(self) -> str:
|
57
|
+
# It is difficult to get app version number from Google Play
|
58
|
+
r = requests.get(
|
59
|
+
"https://apkpure.net/kakaotalk-messenger/com.kakao.talk/versions"
|
60
|
+
)
|
61
|
+
soup = BeautifulSoup(r.text, "html.parser")
|
62
|
+
for li in soup.find_all("li"):
|
63
|
+
a = li.find("a", class_="dt-version-icon")
|
64
|
+
if a is None:
|
65
|
+
continue
|
66
|
+
if "/kakaotalk-messenger/com.kakao.talk/download/" in a.get("href", ""):
|
67
|
+
return a.get("href").split("/")[-1]
|
68
|
+
return "25.2.1"
|
69
|
+
|
55
70
|
def login(self) -> bool:
|
56
71
|
self.cb_msg("Logging in")
|
57
72
|
|
@@ -96,7 +111,6 @@ class GetKakaoAuth:
|
|
96
111
|
"countryCode": self.country_code,
|
97
112
|
"countryIso": self.country_iso,
|
98
113
|
"phoneNumber": self.phone_number,
|
99
|
-
"method": "sms",
|
100
114
|
"termCodes": [],
|
101
115
|
"simPhoneNumber": f"+{self.country_code}{self.phone_number}",
|
102
116
|
}
|
@@ -244,6 +258,21 @@ class GetKakaoAuth:
|
|
244
258
|
self.cb_msg_block(f"Failed at passcode callback: {response.text}")
|
245
259
|
return False
|
246
260
|
|
261
|
+
return True
|
262
|
+
|
263
|
+
def skip_restoration(self) -> bool:
|
264
|
+
self.cb_msg("Skip restoration")
|
265
|
+
|
266
|
+
response = requests.post(
|
267
|
+
"https://katalk.kakao.com/android/account2/skip-restoration",
|
268
|
+
headers=self.headers,
|
269
|
+
)
|
270
|
+
response_json = json.loads(response.text)
|
271
|
+
|
272
|
+
if response_json["status"] != 0:
|
273
|
+
self.cb_msg_block(f"Failed at skip restoration: {response.text}")
|
274
|
+
return False
|
275
|
+
|
247
276
|
self.nickname = response_json.get("viewData", {}).get("nickname")
|
248
277
|
|
249
278
|
if self.nickname is None:
|
@@ -288,6 +317,7 @@ class GetKakaoAuth:
|
|
288
317
|
self.enter_phone,
|
289
318
|
self.confirm_device_change,
|
290
319
|
self.passcode_callback,
|
320
|
+
self.skip_restoration,
|
291
321
|
self.get_profile,
|
292
322
|
)
|
293
323
|
|
@@ -105,7 +105,7 @@ class GetKakaoDesktopAuth:
|
|
105
105
|
return None, msg
|
106
106
|
|
107
107
|
auth_token = None
|
108
|
-
for i in re.finditer(b"
|
108
|
+
for i in re.finditer(b"authorization: ", s):
|
109
109
|
auth_token_addr = i.start() + 15
|
110
110
|
|
111
111
|
auth_token_bytes = s[auth_token_addr : auth_token_addr + 200]
|
@@ -1,4 +1,6 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
|
+
import base64
|
3
|
+
import io
|
2
4
|
import json
|
3
5
|
import os
|
4
6
|
import platform
|
@@ -6,10 +8,11 @@ import shutil
|
|
6
8
|
import socket
|
7
9
|
import subprocess
|
8
10
|
import time
|
9
|
-
from typing import Any, Dict, Optional, Union, cast
|
11
|
+
from typing import Any, Dict, List, Optional, Union, cast
|
10
12
|
|
11
13
|
import requests
|
12
14
|
import websocket
|
15
|
+
from PIL import Image
|
13
16
|
|
14
17
|
# References
|
15
18
|
# https://github.com/yeongbin-jo/python-chromedriver-autoinstaller/blob/master/chromedriver_autoinstaller/utils.py
|
@@ -24,15 +27,32 @@ def get_free_port() -> int:
|
|
24
27
|
|
25
28
|
|
26
29
|
class CRD:
|
27
|
-
def __init__(
|
30
|
+
def __init__(
|
31
|
+
self,
|
32
|
+
chrome_bin: str,
|
33
|
+
port: Optional[int] = None,
|
34
|
+
args: Optional[List[str]] = None,
|
35
|
+
):
|
28
36
|
if port is None:
|
29
37
|
port = get_free_port()
|
30
38
|
self.port = port
|
31
|
-
|
39
|
+
|
40
|
+
launch_cmd: List[str] = []
|
41
|
+
self.display = None
|
42
|
+
if (
|
43
|
+
platform.system() == "Linux"
|
44
|
+
and os.environ.get("DISPLAY", False) is False
|
45
|
+
and shutil.which("xvfb-run")
|
46
|
+
):
|
47
|
+
launch_cmd += ["xvfb-run", "--server-args='-screen 0, 1024x768x24'"]
|
48
|
+
|
49
|
+
launch_cmd += [
|
32
50
|
chrome_bin,
|
33
51
|
f"--remote-debugging-port={port}",
|
34
|
-
f"--remote-allow-origins=http://
|
52
|
+
f"--remote-allow-origins=http://127.0.0.1:{port}",
|
35
53
|
]
|
54
|
+
if args:
|
55
|
+
launch_cmd += args
|
36
56
|
|
37
57
|
# Adding --no-sandbox in Windows may cause Signal fail to launch
|
38
58
|
# https://github.com/laggykiller/sticker-convert/issues/274
|
@@ -40,7 +60,7 @@ class CRD:
|
|
40
60
|
platform.system() != "Windows"
|
41
61
|
and "geteuid" in dir(os)
|
42
62
|
and os.geteuid() == 0
|
43
|
-
):
|
63
|
+
) or os.path.isfile("/.dockerenv"):
|
44
64
|
launch_cmd.append("--no-sandbox")
|
45
65
|
|
46
66
|
self.chrome_proc = subprocess.Popen(launch_cmd)
|
@@ -89,12 +109,17 @@ class CRD:
|
|
89
109
|
return chrome_bin
|
90
110
|
return None
|
91
111
|
|
92
|
-
def connect(self):
|
112
|
+
def connect(self, target_id: int = 0):
|
93
113
|
self.cmd_id = 1
|
94
114
|
r = None
|
115
|
+
targets: List[Any] = []
|
95
116
|
for _ in range(30):
|
96
117
|
try:
|
97
|
-
r = requests.get(f"http://
|
118
|
+
r = requests.get(f"http://127.0.0.1:{self.port}/json")
|
119
|
+
targets = json.loads(r.text)
|
120
|
+
if len(targets) == 0:
|
121
|
+
time.sleep(1)
|
122
|
+
continue
|
98
123
|
break
|
99
124
|
except requests.exceptions.ConnectionError:
|
100
125
|
time.sleep(1)
|
@@ -102,17 +127,12 @@ class CRD:
|
|
102
127
|
if r is None:
|
103
128
|
raise RuntimeError("Cannot connect to chrome debugging port")
|
104
129
|
|
105
|
-
targets = json.loads(r.text)
|
106
|
-
for _ in range(30):
|
107
|
-
if len(targets) == 0:
|
108
|
-
time.sleep(1)
|
109
|
-
else:
|
110
|
-
break
|
111
|
-
|
112
130
|
if len(targets) == 0:
|
113
131
|
raise RuntimeError("Cannot create websocket connection with debugger")
|
114
132
|
|
115
|
-
self.ws = websocket.create_connection(
|
133
|
+
self.ws = websocket.create_connection( # type: ignore
|
134
|
+
targets[target_id]["webSocketDebuggerUrl"]
|
135
|
+
)
|
116
136
|
|
117
137
|
def send_cmd(self, command: Dict[Any, Any]) -> Union[str, bytes]:
|
118
138
|
if command.get("id") is None:
|
@@ -138,6 +158,27 @@ class CRD:
|
|
138
158
|
command["params"]["contextId"] = context_id
|
139
159
|
return self.send_cmd(command)
|
140
160
|
|
161
|
+
def set_transparent_bg(self) -> Union[str, bytes]:
|
162
|
+
command: Dict[str, Any] = {
|
163
|
+
"id": self.cmd_id,
|
164
|
+
"method": "Emulation.setDefaultBackgroundColorOverride",
|
165
|
+
"params": {"color": {"r": 0, "g": 0, "b": 0, "a": 0}},
|
166
|
+
}
|
167
|
+
return self.send_cmd(command)
|
168
|
+
|
169
|
+
def screenshot(self, clip: Optional[Dict[str, int]] = None):
|
170
|
+
command: Dict[str, Any] = {
|
171
|
+
"id": self.cmd_id,
|
172
|
+
"method": "Page.captureScreenshot",
|
173
|
+
"params": {},
|
174
|
+
}
|
175
|
+
if clip:
|
176
|
+
command["params"]["clip"] = clip
|
177
|
+
result = self.send_cmd(command)
|
178
|
+
return Image.open(
|
179
|
+
io.BytesIO(base64.b64decode(json.loads(result)["result"]["data"]))
|
180
|
+
)
|
181
|
+
|
141
182
|
def get_curr_url(self) -> str:
|
142
183
|
r = self.exec_js("window.location.href")
|
143
184
|
return cast(
|
@@ -148,6 +189,26 @@ class CRD:
|
|
148
189
|
command = {"id": self.cmd_id, "method": "Page.navigate", "params": {"url": url}}
|
149
190
|
self.send_cmd(command)
|
150
191
|
|
192
|
+
def open_html_str(self, html: str):
|
193
|
+
command: Dict[str, Any] = {
|
194
|
+
"id": self.cmd_id,
|
195
|
+
"method": "Page.navigate",
|
196
|
+
"params": {"url": "about:blank"},
|
197
|
+
}
|
198
|
+
result = cast(str, self.send_cmd(command))
|
199
|
+
frame_id = json.loads(result).get("result", {}).get("frameId", None)
|
200
|
+
if frame_id is None:
|
201
|
+
raise RuntimeError(f"Cannot navigate to about:blank ({result})")
|
202
|
+
|
203
|
+
self.exec_js('document.getElementsByTagName("html")[0].remove()')
|
204
|
+
|
205
|
+
command = {
|
206
|
+
"id": self.cmd_id,
|
207
|
+
"method": "Page.setDocumentContent",
|
208
|
+
"params": {"frameId": frame_id, "html": html},
|
209
|
+
}
|
210
|
+
self.send_cmd(command)
|
211
|
+
|
151
212
|
def runtime_enable(self):
|
152
213
|
command = {
|
153
214
|
"method": "Runtime.enable",
|
@@ -169,3 +230,5 @@ class CRD:
|
|
169
230
|
def close(self):
|
170
231
|
self.ws.close()
|
171
232
|
self.chrome_proc.kill()
|
233
|
+
if self.display:
|
234
|
+
self.display.stop()
|
@@ -3,16 +3,22 @@ from __future__ import annotations
|
|
3
3
|
|
4
4
|
import json
|
5
5
|
import mmap
|
6
|
+
import warnings
|
6
7
|
from decimal import ROUND_HALF_UP, Decimal
|
7
8
|
from fractions import Fraction
|
8
9
|
from io import BytesIO
|
9
|
-
from math import gcd
|
10
|
+
from math import ceil, gcd
|
10
11
|
from pathlib import Path
|
11
12
|
from typing import BinaryIO, List, Optional, Tuple, Union, cast
|
12
13
|
|
14
|
+
from bs4 import BeautifulSoup, XMLParsedAsHTMLWarning
|
13
15
|
from PIL import Image, UnidentifiedImageError
|
14
16
|
from rlottie_python.rlottie_wrapper import LottieAnimation
|
15
17
|
|
18
|
+
from sticker_convert.definitions import SVG_DEFAULT_HEIGHT, SVG_DEFAULT_WIDTH, SVG_SAMPLE_FPS
|
19
|
+
|
20
|
+
warnings.filterwarnings("ignore", category=XMLParsedAsHTMLWarning)
|
21
|
+
|
16
22
|
|
17
23
|
def lcm(a: int, b: int):
|
18
24
|
return abs(a * b) // gcd(a, b)
|
@@ -90,11 +96,17 @@ class CodecInfo:
|
|
90
96
|
self.file_ext = CodecInfo.get_file_ext(file)
|
91
97
|
else:
|
92
98
|
self.file_ext = file_ext
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
99
|
+
if self.file_ext == ".svg":
|
100
|
+
self.fps, self.frames, self.duration, self.res = CodecInfo.get_svg_info(
|
101
|
+
file
|
102
|
+
)
|
103
|
+
self.codec = "svg"
|
104
|
+
else:
|
105
|
+
self.fps, self.frames, self.duration = (
|
106
|
+
CodecInfo.get_file_fps_frames_duration(file)
|
107
|
+
)
|
108
|
+
self.codec = CodecInfo.get_file_codec(file)
|
109
|
+
self.res = CodecInfo.get_file_res(file)
|
98
110
|
self.is_animated = self.fps > 1
|
99
111
|
|
100
112
|
@staticmethod
|
@@ -435,3 +447,38 @@ class CodecInfo:
|
|
435
447
|
if CodecInfo.get_file_frames(file, check_anim=True) > 1:
|
436
448
|
return True
|
437
449
|
return False
|
450
|
+
|
451
|
+
@staticmethod
|
452
|
+
def get_svg_info(
|
453
|
+
file: Union[Path, bytes],
|
454
|
+
) -> Tuple[float, int, int, Tuple[int, int]]:
|
455
|
+
if isinstance(file, Path):
|
456
|
+
with open(file) as f:
|
457
|
+
svg = f.read()
|
458
|
+
else:
|
459
|
+
svg = file.decode()
|
460
|
+
|
461
|
+
soup = BeautifulSoup(svg, "html.parser")
|
462
|
+
svg_tag = soup.find_all("svg")[0]
|
463
|
+
width = int(svg_tag.get("width", SVG_DEFAULT_WIDTH))
|
464
|
+
height = int(svg_tag.get("height", SVG_DEFAULT_HEIGHT))
|
465
|
+
|
466
|
+
animate_elements = [*soup.find_all("animate")] + [
|
467
|
+
*soup.find_all("animateTransform")
|
468
|
+
]
|
469
|
+
duration = 0
|
470
|
+
for element in animate_elements:
|
471
|
+
dur = cast(str, element.get("dur"))
|
472
|
+
if dur.endswith("s"):
|
473
|
+
duration = int(max(duration, float(dur[:-1]) * 1000))
|
474
|
+
elif dur.endswith("ms"):
|
475
|
+
duration = int(max(duration, float(dur[:-2])))
|
476
|
+
|
477
|
+
if duration != 0:
|
478
|
+
fps = SVG_SAMPLE_FPS
|
479
|
+
frames = ceil(fps * duration / 1000)
|
480
|
+
else:
|
481
|
+
fps = 0
|
482
|
+
frames = 1
|
483
|
+
|
484
|
+
return fps, frames, duration, (width, height)
|
@@ -22,9 +22,7 @@ class UrlDetect:
|
|
22
22
|
):
|
23
23
|
return "line"
|
24
24
|
|
25
|
-
if domain in ("e.kakao.com", "emoticon.kakao.com")
|
26
|
-
"kakaotalk://store/emoticon/"
|
27
|
-
):
|
25
|
+
if domain in ("e.kakao.com", "emoticon.kakao.com"):
|
28
26
|
return "kakao"
|
29
27
|
|
30
28
|
if domain == "stickers.viber.com":
|
sticker_convert/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: sticker-convert
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.12.0
|
4
4
|
Summary: Convert (animated) stickers to/from WhatsApp, Telegram, Signal, Line, Kakao, Viber, Discord, iMessage. Written in Python.
|
5
5
|
Author-email: laggykiller <chaudominic2@gmail.com>
|
6
6
|
Maintainer-email: laggykiller <chaudominic2@gmail.com>
|
@@ -374,7 +374,6 @@ Requires-Dist: httpx~=0.28.1
|
|
374
374
|
Requires-Dist: imagequant~=1.1.4
|
375
375
|
Requires-Dist: memory-tempfile~=2.2.3
|
376
376
|
Requires-Dist: mergedeep~=1.3.4
|
377
|
-
Requires-Dist: mini-racer~=0.12.4
|
378
377
|
Requires-Dist: numpy>=1.22.4
|
379
378
|
Requires-Dist: Pillow~=11.1.0
|
380
379
|
Requires-Dist: pyoxipng~=9.0.0
|
@@ -437,7 +436,7 @@ Dynamic: license-file
|
|
437
436
|
| [Telegram](docs/guide_telegram.md) | ✅ (Require `token` or telethon) | ✅ (Require `token` & `user_id` or telethon or manually) |
|
438
437
|
| [WhatsApp](docs/guide_whatsapp.md) | ⭕ (By Android or WhatsApp Web) | ⭕ (Create `.wastickers`, import by Sticker Maker) |
|
439
438
|
| [Line](docs/guide_line.md) | ✅ | 🚫 (Need to submit for manual approval) |
|
440
|
-
| [Kakao](docs/guide_kakao.md) | ✅ (Need '
|
439
|
+
| [Kakao](docs/guide_kakao.md) | ✅ (Need 'auth_token' for animated) | 🚫 (Need to submit for manual approval) |
|
441
440
|
| [Viber](docs/guide_viber.md) | ✅ | ✅ (Require `viber_auth`) |
|
442
441
|
| [Discord](docs/guide_discord.md) | ✅ (Require `token`) | 🚫 |
|
443
442
|
| [iMessage](docs/guide_imessage.md) | 🚫 | ⭕ (Create Xcode stickerpack project for sideload) |
|
@@ -462,7 +461,7 @@ Dynamic: license-file
|
|
462
461
|
- For more information: https://github.com/doubleplusc/Line-sticker-downloader
|
463
462
|
- Upload: Not supported. You need to manually submit sticker pack for approval before you can use in app.
|
464
463
|
- Kakao
|
465
|
-
- Download: Supported (e.g. `https://e.kakao.com/t/xxxxx`
|
464
|
+
- Download: Supported (e.g. `https://e.kakao.com/t/xxxxx` OR `https://emoticon.kakao.com/items/xxxxx` OR `4404400`). It is rather complicated, learn more from [docs/guide_kakao.md](docs/guide_kakao.md)
|
466
465
|
- Upload: Not supported. You need to manually submit sticker pack for approval before you can use in app.
|
467
466
|
- Viber
|
468
467
|
- Download: Supported (e.g. `https://stickers.viber.com/pages/example` OR `https://stickers.viber.com/pages/custom-sticker-packs/example`)
|
@@ -550,7 +549,7 @@ Input options:
|
|
550
549
|
OR https://line.me/S/sticker/1234/?lang=en OR line://shop/detail/1234 OR 1234)
|
551
550
|
--download-kakao DOWNLOAD_KAKAO
|
552
551
|
Download kakao stickers from a URL / ID as input
|
553
|
-
(Example: https://e.kakao.com/t/xxxxx
|
552
|
+
(Example: https://e.kakao.com/t/xxxxx
|
554
553
|
OR https://emoticon.kakao.com/items/xxxxx OR 4404400)
|
555
554
|
--download-viber DOWNLOAD_VIBER
|
556
555
|
Download viber stickers from a URL as input
|
@@ -1,20 +1,20 @@
|
|
1
1
|
sticker_convert/__init__.py,sha256=iQnv6UOOA69c3soAn7ZOnAIubTIQSUxtq1Uhh8xRWvU,102
|
2
2
|
sticker_convert/__main__.py,sha256=elDCMvU27letiYs8jPUpxaCq5puURnvcDuqGsAAb6_w,592
|
3
3
|
sticker_convert/cli.py,sha256=MWzq9QJJ7HYBD73DmSgRIPo-u58mTGEIoUUpJKNjI0Q,22358
|
4
|
-
sticker_convert/converter.py,sha256=
|
5
|
-
sticker_convert/definitions.py,sha256=
|
4
|
+
sticker_convert/converter.py,sha256=Dnl5nkvSgIvfbcKp0n4ytoW6ZeHim5dNnrhAs3MZXx8,39423
|
5
|
+
sticker_convert/definitions.py,sha256=BqROmOvqIqw8ANaqZXIxJAXGD0HgjAvCfFouQ4SaWHc,2254
|
6
6
|
sticker_convert/gui.py,sha256=D9L-lFif4P4MANglis_wpjkzlUuGPuq8AybWGyPVP8E,33824
|
7
7
|
sticker_convert/job.py,sha256=gEd6yF8WPUR1_7q9SVhnwdD5usrDxwowNnR5q9uS8Yc,28011
|
8
8
|
sticker_convert/job_option.py,sha256=NysZtKVVG7EzRkLnVaeYJY0uNlYKFmjoststB1cvVrY,8020
|
9
|
-
sticker_convert/version.py,sha256=
|
9
|
+
sticker_convert/version.py,sha256=5PwAcytiNCTA-jXWUYxsOwxsKuNWAlGiQx6_QL36xdI,47
|
10
10
|
sticker_convert/downloaders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
11
|
sticker_convert/downloaders/download_base.py,sha256=MI5pCT_tkfoaFlrD1oNynDj1Rv1CK0APuNVElTEAEis,5110
|
12
12
|
sticker_convert/downloaders/download_discord.py,sha256=6AFpLAYL2hRvVcsqUtzDUC31U66U02ZcnKXDnZRi2jk,3496
|
13
|
-
sticker_convert/downloaders/download_kakao.py,sha256=
|
13
|
+
sticker_convert/downloaders/download_kakao.py,sha256=kcWwyid76Db7QR8MqhHJuYONnIY9zOHRUBcNcae1VYI,12465
|
14
14
|
sticker_convert/downloaders/download_line.py,sha256=c-hTzEarGQP0b-pnPl29NuSKcaZWUeRo_94YpJzz72M,17911
|
15
15
|
sticker_convert/downloaders/download_signal.py,sha256=3wv-BLd4qggly4AdtwV8f3vUpCVZ-8GnoPLoWngY3Pk,3728
|
16
16
|
sticker_convert/downloaders/download_telegram.py,sha256=iGmOcVjU2Ai19t1lyDY2JhQIc--O5XMyNCGXZAA4DVY,2028
|
17
|
-
sticker_convert/downloaders/download_viber.py,sha256=
|
17
|
+
sticker_convert/downloaders/download_viber.py,sha256=ApHy4sDUOmVwplHfLbso_gyOKn9zccqvRnHENzR_UUM,3790
|
18
18
|
sticker_convert/gui_components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
19
|
sticker_convert/gui_components/gui_utils.py,sha256=okho2cA1Scem_m6rPiYifreFzpFrM21-yUkiAv64EUI,3431
|
20
20
|
sticker_convert/gui_components/frames/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -73,7 +73,7 @@ sticker_convert/resources/appicon.png,sha256=6XBEQz7PnerqS43aRkwpWolFG4WvKMuQ-st
|
|
73
73
|
sticker_convert/resources/compression.json,sha256=hZY906wUmdAuflVhUowm9EW6h40BzuD4LBNe-Mpd9V8,14184
|
74
74
|
sticker_convert/resources/emoji.json,sha256=q9DRFdVJfm7feZW7ZM6Xa5P1QsMvrn0PbBVU9jKcJKI,422720
|
75
75
|
sticker_convert/resources/help.json,sha256=oI8OIB0aFKgDw4bTKT69zDNshOjf0-bM5el3nbJTzMs,7225
|
76
|
-
sticker_convert/resources/input.json,sha256=
|
76
|
+
sticker_convert/resources/input.json,sha256=BYHYDqMLZuQnA3MF0ViudGpkSuAG8OMS7L-7hxC3K3o,3858
|
77
77
|
sticker_convert/resources/memdump_linux.sh,sha256=YbdX5C5RyCnoeDUE6JgTo8nQXKhrUw5-kFDx5bQp9tY,651
|
78
78
|
sticker_convert/resources/memdump_windows.ps1,sha256=CfyNSSEW3HJOkTu-mKrP3qh5aprN-1VCBfj-R1fELA0,302
|
79
79
|
sticker_convert/resources/output.json,sha256=SC_3vtEBJ79R7q0vy_p33eLsGFEyBsSOai0Hy7mLqG8,2208
|
@@ -82,16 +82,16 @@ sticker_convert/uploaders/compress_wastickers.py,sha256=Rgl3WzIDJn7oa00nNqeBFDqI
|
|
82
82
|
sticker_convert/uploaders/upload_base.py,sha256=uQupPn6r4zrlAzpKzzX7CgvZb69ATyrwPKahWOQj0ds,1203
|
83
83
|
sticker_convert/uploaders/upload_signal.py,sha256=k9XS6oU0gYCXEph-LzWvUP3IDINAVaQzzzNgDRp_YNM,7443
|
84
84
|
sticker_convert/uploaders/upload_telegram.py,sha256=WlUyLJlW83XZz6RhA76jHMXA6TNUIEVbPwhi14RTnds,12482
|
85
|
-
sticker_convert/uploaders/upload_viber.py,sha256=
|
85
|
+
sticker_convert/uploaders/upload_viber.py,sha256=E5gXxPF22TyfSyOEe1uxnbonCMgH8ma3WTm0ecJ3yxE,6444
|
86
86
|
sticker_convert/uploaders/xcode_imessage.py,sha256=iTTT8gDYOTNkKqXeSWUBuWfxu7xeE418t2Z1YQFR5L0,11365
|
87
87
|
sticker_convert/utils/callback.py,sha256=spYUGlklOs1yPZAxoqwOWgR1sdimpfM8a27if3TaVYk,6155
|
88
|
-
sticker_convert/utils/chrome_remotedebug.py,sha256=
|
88
|
+
sticker_convert/utils/chrome_remotedebug.py,sha256=jxqH2LktA9UVJW055QeHecVRkc8Hl7KlUcxi_srhDXk,7143
|
89
89
|
sticker_convert/utils/emoji.py,sha256=AqB26JY-PkYzNwPLReSnqLiQKe-bR9UXnLclAbgubJ8,367
|
90
90
|
sticker_convert/utils/process.py,sha256=EAQZ9WpiKmkvToIv8G1HNY4V7m0jXyyePTmeP2XOZzE,4688
|
91
|
-
sticker_convert/utils/url_detect.py,sha256=
|
91
|
+
sticker_convert/utils/url_detect.py,sha256=vCbhQbcW1X_UtdfQlICGY8pMX34KQ6sCtDJZbp0NrSg,876
|
92
92
|
sticker_convert/utils/auth/get_discord_auth.py,sha256=i5SaXU9Ly6Ci_iBYCYIPcQB5FaAehi9DidB4jbKJMVg,4215
|
93
|
-
sticker_convert/utils/auth/get_kakao_auth.py,sha256=
|
94
|
-
sticker_convert/utils/auth/get_kakao_desktop_auth.py,sha256=
|
93
|
+
sticker_convert/utils/auth/get_kakao_auth.py,sha256=Wok5sp0GGBgTwwlfYB7lVq82ndBAOGTcqsWEaWAXFNE,10760
|
94
|
+
sticker_convert/utils/auth/get_kakao_desktop_auth.py,sha256=BBfFI08cVOmi6akenQwMI0Uiatmn76wI8QYHnbvopxg,5809
|
95
95
|
sticker_convert/utils/auth/get_line_auth.py,sha256=8l8ha2vQmk3rHGvDE7PkcxQXbH3oe62LKbI3qVUtvqc,2196
|
96
96
|
sticker_convert/utils/auth/get_signal_auth.py,sha256=oDTcIUcRM8_zfmR6UoBvzBhIscwLRe7n2zw4aw0j8_Q,4564
|
97
97
|
sticker_convert/utils/auth/get_viber_auth.py,sha256=mUTrcxq5bTrzSXEVaeTPqVQIdZdwvIhrbMgBUb7dU30,8173
|
@@ -100,16 +100,16 @@ sticker_convert/utils/auth/telethon_setup.py,sha256=3hH0KglsotFBxT3gwFjHiDuREyah
|
|
100
100
|
sticker_convert/utils/files/cache_store.py,sha256=etfe614OAhAyrnM5fGeESKq6R88YLNqkqkxSzEmZ0V0,1047
|
101
101
|
sticker_convert/utils/files/json_manager.py,sha256=Vr6pZJdLMkrJJWN99210aduVHb0ILyf0SSTaw4TZqgc,541
|
102
102
|
sticker_convert/utils/files/json_resources_loader.py,sha256=flZFixUXRTrOAhvRQpuSQgmJ69yXL94sxukcowLT1JQ,1049
|
103
|
-
sticker_convert/utils/files/metadata_handler.py,sha256=
|
103
|
+
sticker_convert/utils/files/metadata_handler.py,sha256=TUseSpKsFLoqqFf7YFENzkwDYSDKPiMCoEp04ebnVr8,10120
|
104
104
|
sticker_convert/utils/files/run_bin.py,sha256=C_KKGtMUTajJnKo4Ia9e6WuWCXSQRdGsVPG-r5GXT_I,1784
|
105
105
|
sticker_convert/utils/files/sanitize_filename.py,sha256=HBklPGsHRJjFQUIC5rYTQsUrsuTtezZXIEA8CPhLP8A,2156
|
106
106
|
sticker_convert/utils/media/apple_png_normalize.py,sha256=LbrQhc7LlYX4I9ek4XJsZE4l0MygBA1jB-PFiYLEkzk,3657
|
107
|
-
sticker_convert/utils/media/codec_info.py,sha256=
|
107
|
+
sticker_convert/utils/media/codec_info.py,sha256=_F_iV3k2GYtb8vYZUSKrWl9E7XEW6LDywbnJ-DM6kbk,16476
|
108
108
|
sticker_convert/utils/media/decrypt_kakao.py,sha256=4wq9ZDRnFkx1WmFZnyEogBofiLGsWQM_X69HlA36578,1947
|
109
109
|
sticker_convert/utils/media/format_verify.py,sha256=oM32P186tWe9YxvBQRPr8D3FEmBN3b2rEe_2S_MwxyQ,6236
|
110
|
-
sticker_convert-2.
|
111
|
-
sticker_convert-2.
|
112
|
-
sticker_convert-2.
|
113
|
-
sticker_convert-2.
|
114
|
-
sticker_convert-2.
|
115
|
-
sticker_convert-2.
|
110
|
+
sticker_convert-2.12.0.dist-info/licenses/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
|
111
|
+
sticker_convert-2.12.0.dist-info/METADATA,sha256=KBvqYSm9HFUCkPrFOqZ3RDVKCI6916VpywSKT1JvwsE,53484
|
112
|
+
sticker_convert-2.12.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
113
|
+
sticker_convert-2.12.0.dist-info/entry_points.txt,sha256=MNJ7XyC--ugxi5jS1nzjDLGnxCyLuaGdsVLnJhDHCqs,66
|
114
|
+
sticker_convert-2.12.0.dist-info/top_level.txt,sha256=r9vfnB0l1ZnH5pTH5RvkobnK3Ow9m0RsncaOMAtiAtk,16
|
115
|
+
sticker_convert-2.12.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|