sticker-convert 2.8.2__py3-none-any.whl → 2.8.4__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- sticker_convert/converter.py +91 -43
- sticker_convert/downloaders/download_signal.py +7 -7
- sticker_convert/uploaders/compress_wastickers.py +3 -3
- sticker_convert/uploaders/upload_signal.py +10 -10
- sticker_convert/uploaders/upload_telegram.py +91 -65
- sticker_convert/utils/auth/get_line_auth.py +15 -21
- sticker_convert/utils/files/metadata_handler.py +5 -0
- sticker_convert/version.py +1 -1
- {sticker_convert-2.8.2.dist-info → sticker_convert-2.8.4.dist-info}/METADATA +6 -6
- {sticker_convert-2.8.2.dist-info → sticker_convert-2.8.4.dist-info}/RECORD +14 -14
- {sticker_convert-2.8.2.dist-info → sticker_convert-2.8.4.dist-info}/LICENSE +0 -0
- {sticker_convert-2.8.2.dist-info → sticker_convert-2.8.4.dist-info}/WHEEL +0 -0
- {sticker_convert-2.8.2.dist-info → sticker_convert-2.8.4.dist-info}/entry_points.txt +0 -0
- {sticker_convert-2.8.2.dist-info → sticker_convert-2.8.4.dist-info}/top_level.txt +0 -0
sticker_convert/converter.py
CHANGED
@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Uni
|
|
9
9
|
import numpy as np
|
10
10
|
from PIL import Image
|
11
11
|
from PIL import __version__ as PillowVersion
|
12
|
+
from PIL import features
|
12
13
|
|
13
14
|
from sticker_convert.job_option import CompOption
|
14
15
|
from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
|
@@ -32,6 +33,17 @@ MSG_FAIL_COMP = (
|
|
32
33
|
"[F] Failed Compression {} -> {}, "
|
33
34
|
"cannot get below limit {} with lowest quality under current settings (Best size: {})"
|
34
35
|
)
|
36
|
+
MSG_PYWEBP_DUPFRAME = (
|
37
|
+
"[W] {} contains duplicated frame.\n"
|
38
|
+
" System WebP>=0.5.0 was not found, hence Pillow cannot be used\n"
|
39
|
+
" for creating animated webp. Using pywebp instead, which is known to\n"
|
40
|
+
" collapse same frames into single frame, causing problem with animation timing."
|
41
|
+
)
|
42
|
+
MSG_WEBP_PIL_DUPFRAME = (
|
43
|
+
"[W] {} contains duplicated frame.\n"
|
44
|
+
" Using Pillow to create animated webp to avoid same frames collapse\n"
|
45
|
+
" into single frame, but this is slower."
|
46
|
+
)
|
35
47
|
|
36
48
|
YUV_RGB_MATRIX = np.array(
|
37
49
|
[
|
@@ -41,6 +53,10 @@ YUV_RGB_MATRIX = np.array(
|
|
41
53
|
]
|
42
54
|
)
|
43
55
|
|
56
|
+
# Whether animated WebP is supported
|
57
|
+
# See https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#saving-sequences
|
58
|
+
PIL_WEBP_ANIM = cast(bool, features.check("webp_anim")) # type: ignore
|
59
|
+
|
44
60
|
|
45
61
|
def get_step_value(
|
46
62
|
max_step: Optional[int],
|
@@ -179,6 +195,8 @@ class StickerConvert:
|
|
179
195
|
self.result_step: Optional[int] = None
|
180
196
|
|
181
197
|
self.apngasm = None
|
198
|
+
self.msg_pywebp_dupframe_displayed = False
|
199
|
+
self.msg_webp_pil_dupframe_displayed = False
|
182
200
|
|
183
201
|
@staticmethod
|
184
202
|
def convert(
|
@@ -394,7 +412,7 @@ class StickerConvert:
|
|
394
412
|
with open(self.out_f, "wb+") as f:
|
395
413
|
f.write(data)
|
396
414
|
|
397
|
-
if result_step:
|
415
|
+
if result_step is not None:
|
398
416
|
msg = MSG_DONE_COMP.format(
|
399
417
|
self.in_f_name, self.out_f_name, self.result_size, result_step
|
400
418
|
)
|
@@ -678,15 +696,52 @@ class StickerConvert:
|
|
678
696
|
self._frames_export_apng()
|
679
697
|
else:
|
680
698
|
self._frames_export_png()
|
681
|
-
elif self.out_f.suffix == ".webp"
|
699
|
+
elif self.out_f.suffix == ".webp":
|
682
700
|
self._frames_export_webp()
|
683
701
|
elif self.out_f.suffix == ".gif":
|
684
|
-
self.
|
702
|
+
self._frames_export_pil_anim()
|
685
703
|
elif self.out_f.suffix in (".webm", ".mp4", ".mkv") or is_animated:
|
686
704
|
self._frames_export_pyav()
|
687
705
|
else:
|
688
706
|
self._frames_export_pil()
|
689
707
|
|
708
|
+
def _check_dup(self) -> bool:
|
709
|
+
if len(self.frames_processed) == 1:
|
710
|
+
return False
|
711
|
+
|
712
|
+
prev_frame = self.frames_processed[0]
|
713
|
+
for frame in self.frames_processed[1:]:
|
714
|
+
if np.array_equal(frame, prev_frame):
|
715
|
+
return True
|
716
|
+
prev_frame = frame
|
717
|
+
|
718
|
+
return False
|
719
|
+
|
720
|
+
def _frames_export_webp(self) -> None:
|
721
|
+
has_dup_frames = self._check_dup()
|
722
|
+
if self.fps:
|
723
|
+
# It was noted that pywebp would collapse all frames.
|
724
|
+
# aed005b attempted to fix this by creating webp with
|
725
|
+
# variable frame duration. However, the webp created would
|
726
|
+
# not be accepted by WhatsApp.
|
727
|
+
# Therefore, we are preferring Pillow over pywebp.
|
728
|
+
if has_dup_frames:
|
729
|
+
if PIL_WEBP_ANIM:
|
730
|
+
# Warn that using Pillow is slower
|
731
|
+
if not self.msg_webp_pil_dupframe_displayed:
|
732
|
+
self.cb.put(MSG_WEBP_PIL_DUPFRAME.format(self.in_f_name))
|
733
|
+
self.msg_webp_pil_dupframe_displayed = True
|
734
|
+
self._frames_export_pil_anim()
|
735
|
+
else:
|
736
|
+
if not self.msg_pywebp_dupframe_displayed:
|
737
|
+
self.cb.put(MSG_PYWEBP_DUPFRAME.format(self.in_f_name))
|
738
|
+
self.msg_pywebp_dupframe_displayed = True
|
739
|
+
self._frames_export_pywebp()
|
740
|
+
else:
|
741
|
+
self._frames_export_pywebp()
|
742
|
+
else:
|
743
|
+
self._frames_export_pil()
|
744
|
+
|
690
745
|
def _frames_export_pil(self) -> None:
|
691
746
|
with Image.fromarray(self.frames_processed[0]) as im: # type: ignore
|
692
747
|
im.save(
|
@@ -710,7 +765,7 @@ class StickerConvert:
|
|
710
765
|
codec = "apng"
|
711
766
|
pixel_format = "rgba"
|
712
767
|
options["plays"] = "0"
|
713
|
-
elif self.out_f.suffix in (".
|
768
|
+
elif self.out_f.suffix in (".webm", ".mkv"):
|
714
769
|
codec = "libvpx-vp9"
|
715
770
|
pixel_format = "yuva420p"
|
716
771
|
options["loop"] = "0"
|
@@ -734,7 +789,7 @@ class StickerConvert:
|
|
734
789
|
output.mux(out_stream.encode(av_frame))
|
735
790
|
output.mux(out_stream.encode())
|
736
791
|
|
737
|
-
def
|
792
|
+
def _frames_export_pil_anim(self) -> None:
|
738
793
|
extra_kwargs: Dict[str, Any] = {}
|
739
794
|
|
740
795
|
# disposal=2 on gif cause flicker in image with transparency
|
@@ -745,25 +800,34 @@ class StickerConvert:
|
|
745
800
|
else:
|
746
801
|
extra_kwargs["optimize"] = True
|
747
802
|
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
803
|
+
if self.out_f.suffix == ".gif":
|
804
|
+
# GIF can only have one alpha color
|
805
|
+
# Change lowest alpha to alpha=0
|
806
|
+
# Only keep alpha=0 and alpha=255, nothing in between
|
807
|
+
frames_processed = np.array(self.frames_processed)
|
808
|
+
alpha = frames_processed[:, :, :, 3]
|
809
|
+
alpha_min = np.min(alpha) # type: ignore
|
810
|
+
if alpha_min < 255:
|
811
|
+
alpha[alpha > alpha_min] = 255
|
812
|
+
alpha[alpha == alpha_min] = 0
|
813
|
+
|
814
|
+
if 0 in alpha:
|
815
|
+
extra_kwargs["transparency"] = 0
|
816
|
+
extra_kwargs["disposal"] = 2
|
817
|
+
im_out = [self.quantize(Image.fromarray(i)) for i in frames_processed] # type: ignore
|
818
|
+
else:
|
819
|
+
im_out = [
|
820
|
+
self.quantize(Image.fromarray(i).convert("RGB")).convert("RGB") # type: ignore
|
821
|
+
for i in frames_processed
|
822
|
+
]
|
823
|
+
extra_kwargs["format"] = "GIF"
|
824
|
+
elif self.out_f.suffix == ".webp":
|
825
|
+
im_out = [Image.fromarray(i) for i in self.frames_processed] # type: ignore
|
826
|
+
extra_kwargs["format"] = "WebP"
|
827
|
+
extra_kwargs["minimize_size"] = True
|
828
|
+
extra_kwargs["method"] = 6
|
762
829
|
else:
|
763
|
-
|
764
|
-
self.quantize(Image.fromarray(i).convert("RGB")).convert("RGB") # type: ignore
|
765
|
-
for i in frames_processed
|
766
|
-
]
|
830
|
+
raise RuntimeError(f"Invalid format {self.out_f.suffix}")
|
767
831
|
|
768
832
|
if self.fps:
|
769
833
|
extra_kwargs["save_all"] = True
|
@@ -773,12 +837,11 @@ class StickerConvert:
|
|
773
837
|
|
774
838
|
im_out[0].save(
|
775
839
|
self.tmp_f,
|
776
|
-
format="GIF",
|
777
840
|
quality=self.quality,
|
778
841
|
**extra_kwargs,
|
779
842
|
)
|
780
843
|
|
781
|
-
def
|
844
|
+
def _frames_export_pywebp(self) -> None:
|
782
845
|
import webp # type: ignore
|
783
846
|
|
784
847
|
assert self.fps
|
@@ -786,25 +849,10 @@ class StickerConvert:
|
|
786
849
|
config = webp.WebPConfig.new(quality=self.quality) # type: ignore
|
787
850
|
enc = webp.WebPAnimEncoder.new(self.res_w, self.res_h) # type: ignore
|
788
851
|
timestamp_ms = 0
|
789
|
-
|
790
|
-
|
791
|
-
pic = webp.WebPPicture.from_numpy(self.frames_processed[0]) # type: ignore
|
792
|
-
enc.encode_frame(pic, 0, config=config) # type: ignore
|
793
|
-
|
794
|
-
frame_num = 1
|
795
|
-
frame_num_prev = 1
|
796
|
-
frame_total = len(self.frames_processed)
|
797
|
-
while frame_num < frame_total - 1:
|
798
|
-
while frame_num < frame_total - 1 and np.array_equal(
|
799
|
-
self.frames_processed[frame_num_prev],
|
800
|
-
self.frames_processed[frame_num],
|
801
|
-
):
|
802
|
-
timestamp_ms += timestamp_inc
|
803
|
-
frame_num += 1
|
804
|
-
pic = webp.WebPPicture.from_numpy(self.frames_processed[frame_num]) # type: ignore
|
852
|
+
for frame in self.frames_processed:
|
853
|
+
pic = webp.WebPPicture.from_numpy(frame) # type: ignore
|
805
854
|
enc.encode_frame(pic, timestamp_ms, config=config) # type: ignore
|
806
|
-
|
807
|
-
|
855
|
+
timestamp_ms += int(1000 / self.fps)
|
808
856
|
anim_data = enc.assemble(timestamp_ms) # type: ignore
|
809
857
|
self.tmp_f.write(anim_data.buffer()) # type: ignore
|
810
858
|
|
@@ -3,9 +3,9 @@ from pathlib import Path
|
|
3
3
|
from typing import Dict, Optional
|
4
4
|
|
5
5
|
import anyio
|
6
|
-
from signalstickers_client import
|
7
|
-
from signalstickers_client.
|
8
|
-
from signalstickers_client.
|
6
|
+
from signalstickers_client.errors import SignalException
|
7
|
+
from signalstickers_client.models import StickerPack
|
8
|
+
from signalstickers_client.stickersclient import StickersClient
|
9
9
|
|
10
10
|
from sticker_convert.downloaders.download_base import DownloadBase
|
11
11
|
from sticker_convert.job_option import CredOption
|
@@ -21,7 +21,7 @@ class DownloadSignal(DownloadBase):
|
|
21
21
|
@staticmethod
|
22
22
|
async def get_pack(pack_id: str, pack_key: str) -> StickerPack:
|
23
23
|
async with StickersClient() as client:
|
24
|
-
pack = await client.get_pack(pack_id, pack_key)
|
24
|
+
pack = await client.get_pack(pack_id, pack_key)
|
25
25
|
|
26
26
|
return pack
|
27
27
|
|
@@ -32,14 +32,14 @@ class DownloadSignal(DownloadBase):
|
|
32
32
|
None,
|
33
33
|
{
|
34
34
|
"set_progress_mode": "determinate",
|
35
|
-
"steps": len(pack.stickers),
|
35
|
+
"steps": len(pack.stickers),
|
36
36
|
},
|
37
37
|
)
|
38
38
|
)
|
39
39
|
|
40
40
|
emoji_dict: Dict[str, str] = {}
|
41
|
-
for sticker in pack.stickers:
|
42
|
-
f_id = str(sticker.id).zfill(3)
|
41
|
+
for sticker in pack.stickers:
|
42
|
+
f_id = str(sticker.id).zfill(3)
|
43
43
|
f_path = Path(self.out_dir, f_id)
|
44
44
|
with open(f_path, "wb") as f:
|
45
45
|
f.write(sticker.image_data) # type: ignore
|
@@ -79,7 +79,7 @@ class CompressWastickers(UploadBase):
|
|
79
79
|
else:
|
80
80
|
ext = ".png"
|
81
81
|
|
82
|
-
dst = Path(tempdir,
|
82
|
+
dst = Path(tempdir, f"sticker_{num+1}{ext}")
|
83
83
|
|
84
84
|
if FormatVerify.check_file(
|
85
85
|
src, spec=self.webp_spec
|
@@ -114,7 +114,7 @@ class CompressWastickers(UploadBase):
|
|
114
114
|
opt_comp_merged.merge(self.spec_cover)
|
115
115
|
|
116
116
|
cover_path_old = MetadataHandler.get_cover(self.opt_output.dir)
|
117
|
-
cover_path_new = Path(pack_dir, "
|
117
|
+
cover_path_new = Path(pack_dir, "tray.png")
|
118
118
|
if cover_path_old:
|
119
119
|
if FormatVerify.check_file(cover_path_old, spec=self.spec_cover):
|
120
120
|
shutil.copy(cover_path_old, cover_path_new)
|
@@ -142,7 +142,7 @@ class CompressWastickers(UploadBase):
|
|
142
142
|
self.cb_return,
|
143
143
|
)
|
144
144
|
|
145
|
-
MetadataHandler.set_metadata(pack_dir, author=author, title=title)
|
145
|
+
MetadataHandler.set_metadata(pack_dir, author=author, title=title, newline=True)
|
146
146
|
|
147
147
|
@staticmethod
|
148
148
|
def start(
|
@@ -4,9 +4,9 @@ from pathlib import Path
|
|
4
4
|
from typing import Any, Dict, List
|
5
5
|
|
6
6
|
import anyio
|
7
|
-
from signalstickers_client import
|
8
|
-
from signalstickers_client.
|
9
|
-
from signalstickers_client.
|
7
|
+
from signalstickers_client.errors import SignalException
|
8
|
+
from signalstickers_client.models import LocalStickerPack, Sticker
|
9
|
+
from signalstickers_client.stickersclient import StickersClient
|
10
10
|
|
11
11
|
from sticker_convert.converter import StickerConvert
|
12
12
|
from sticker_convert.job_option import CompOption, CredOption, OutputOption
|
@@ -39,7 +39,7 @@ class UploadSignal(UploadBase):
|
|
39
39
|
@staticmethod
|
40
40
|
async def upload_pack(pack: LocalStickerPack, uuid: str, password: str) -> str:
|
41
41
|
async with StickersClient(uuid, password) as client:
|
42
|
-
pack_id, pack_key = await client.upload_pack(pack)
|
42
|
+
pack_id, pack_key = await client.upload_pack(pack)
|
43
43
|
|
44
44
|
result = (
|
45
45
|
f"https://signal.art/addstickers/#pack_id={pack_id}&pack_key={pack_key}"
|
@@ -53,7 +53,7 @@ class UploadSignal(UploadBase):
|
|
53
53
|
self.cb.put(f"Verifying {src} for uploading to signal")
|
54
54
|
|
55
55
|
sticker = Sticker()
|
56
|
-
sticker.id = pack.nb_stickers
|
56
|
+
sticker.id = pack.nb_stickers
|
57
57
|
|
58
58
|
emoji = emoji_dict.get(Path(src).stem, None)
|
59
59
|
if not emoji:
|
@@ -61,7 +61,7 @@ class UploadSignal(UploadBase):
|
|
61
61
|
f"Warning: Cannot find emoji for file {Path(src).name}, skip uploading this file..."
|
62
62
|
)
|
63
63
|
continue
|
64
|
-
sticker.emoji = emoji[:1]
|
64
|
+
sticker.emoji = emoji[:1]
|
65
65
|
|
66
66
|
if Path(src).suffix == ".webp":
|
67
67
|
spec_choice = self.webp_spec
|
@@ -84,10 +84,10 @@ class UploadSignal(UploadBase):
|
|
84
84
|
|
85
85
|
assert isinstance(image_data, bytes)
|
86
86
|
|
87
|
-
sticker.image_data = image_data
|
87
|
+
sticker.image_data = image_data
|
88
88
|
else:
|
89
89
|
with open(src, "rb") as f:
|
90
|
-
sticker.image_data = f.read()
|
90
|
+
sticker.image_data = f.read()
|
91
91
|
|
92
92
|
pack._addsticker(sticker) # type: ignore
|
93
93
|
|
@@ -140,8 +140,8 @@ class UploadSignal(UploadBase):
|
|
140
140
|
)
|
141
141
|
for pack_title, stickers in packs.items():
|
142
142
|
pack = LocalStickerPack()
|
143
|
-
pack.title = pack_title
|
144
|
-
pack.author = author
|
143
|
+
pack.title = pack_title
|
144
|
+
pack.author = author
|
145
145
|
|
146
146
|
self.add_stickers_to_pack(pack, stickers, emoji_dict)
|
147
147
|
self.cb.put(f"Uploading pack {pack_title}")
|
@@ -2,7 +2,7 @@
|
|
2
2
|
import copy
|
3
3
|
import re
|
4
4
|
from pathlib import Path
|
5
|
-
from typing import Any, Dict, List, Optional,
|
5
|
+
from typing import Any, Dict, List, Optional, Union, cast
|
6
6
|
|
7
7
|
import anyio
|
8
8
|
from telegram import InputSticker, Sticker
|
@@ -96,7 +96,7 @@ class UploadTelegram(UploadBase):
|
|
96
96
|
"[^0-9a-zA-Z]+", "_", pack_short_name
|
97
97
|
) # name used in url, only alphanum and underscore only
|
98
98
|
|
99
|
-
sticker_set = None
|
99
|
+
sticker_set: Any = None
|
100
100
|
try:
|
101
101
|
sticker_set = await bot.get_sticker_set(
|
102
102
|
pack_short_name,
|
@@ -152,11 +152,10 @@ class UploadTelegram(UploadBase):
|
|
152
152
|
else:
|
153
153
|
sticker_type = Sticker.REGULAR
|
154
154
|
|
155
|
-
|
155
|
+
init_input_stickers: List[InputSticker] = []
|
156
156
|
sticker_format = None
|
157
|
-
|
158
|
-
|
159
|
-
for src in stickers:
|
157
|
+
sticker_format_prev = None
|
158
|
+
for count, src in enumerate(stickers):
|
160
159
|
self.cb.put(f"Verifying {src} for uploading to telegram")
|
161
160
|
|
162
161
|
emoji = emoji_dict.get(Path(src).stem, None)
|
@@ -175,16 +174,13 @@ class UploadTelegram(UploadBase):
|
|
175
174
|
ext = Path(src).suffix
|
176
175
|
if ext == ".tgs":
|
177
176
|
spec_choice = self.tgs_spec
|
178
|
-
cover_spec_choice = self.tgs_cover_spec
|
179
177
|
sticker_format = "animated"
|
180
178
|
elif ext == ".webm":
|
181
179
|
spec_choice = self.webm_spec
|
182
|
-
cover_spec_choice = self.webm_cover_spec
|
183
180
|
sticker_format = "video"
|
184
181
|
else:
|
185
182
|
ext = ".png"
|
186
183
|
spec_choice = self.png_spec
|
187
|
-
cover_spec_choice = self.png_cover_spec
|
188
184
|
sticker_format = "static"
|
189
185
|
|
190
186
|
if self.opt_output.option == "telegram_emoji":
|
@@ -203,81 +199,98 @@ class UploadTelegram(UploadBase):
|
|
203
199
|
)
|
204
200
|
sticker_bytes = cast(bytes, convert_result)
|
205
201
|
|
206
|
-
|
207
|
-
|
202
|
+
input_sticker = InputSticker(
|
203
|
+
sticker=sticker_bytes,
|
204
|
+
emoji_list=emoji_list,
|
205
|
+
format=sticker_format,
|
208
206
|
)
|
209
207
|
|
208
|
+
if sticker_set is None:
|
209
|
+
if count < 50 and (
|
210
|
+
sticker_format_prev is None
|
211
|
+
or sticker_format_prev == sticker_format
|
212
|
+
):
|
213
|
+
init_input_stickers.append(input_sticker)
|
214
|
+
else:
|
215
|
+
start_msg = f"Creating pack and bulk uploading {count} stickers with same format of {pack_short_name}"
|
216
|
+
finish_msg = f"Created pack and bulk uploaded {count} stickers with same format of {pack_short_name}"
|
217
|
+
error_msg = f"Cannot create pack and bulk upload {count} stickers with same format of {pack_short_name} due to"
|
218
|
+
self.cb.put(start_msg)
|
219
|
+
try:
|
220
|
+
await bot.create_new_sticker_set(
|
221
|
+
user_id=self.telegram_userid,
|
222
|
+
name=pack_short_name,
|
223
|
+
title=pack_title,
|
224
|
+
stickers=init_input_stickers,
|
225
|
+
sticker_type=sticker_type,
|
226
|
+
)
|
227
|
+
sticker_set = True
|
228
|
+
self.cb.put(finish_msg)
|
229
|
+
except TelegramError as e:
|
230
|
+
self.cb.put(f"{error_msg} {e}")
|
231
|
+
return None
|
232
|
+
else:
|
233
|
+
try:
|
234
|
+
# We could use tg.start_soon() here
|
235
|
+
# But this would disrupt the order of stickers
|
236
|
+
await bot.add_sticker_to_set(
|
237
|
+
user_id=self.telegram_userid,
|
238
|
+
name=pack_short_name,
|
239
|
+
sticker=input_sticker,
|
240
|
+
)
|
241
|
+
self.cb.put(f"Uploaded sticker {src} of {pack_short_name}")
|
242
|
+
except BadRequest as e:
|
243
|
+
self.cb.put(
|
244
|
+
f"Cannot upload sticker {src} of {pack_short_name} due to {e}"
|
245
|
+
)
|
246
|
+
if str(e) == "Stickerpack_not_found":
|
247
|
+
self.cb.put(
|
248
|
+
"Hint: You might had deleted and recreated pack too quickly. Wait about 3 minutes and try again."
|
249
|
+
)
|
250
|
+
except TelegramError as e:
|
251
|
+
self.cb.put(
|
252
|
+
f"Cannot upload sticker {src} of {pack_short_name} due to {e}"
|
253
|
+
)
|
254
|
+
|
255
|
+
sticker_format_prev = sticker_format
|
256
|
+
|
210
257
|
cover_path = MetadataHandler.get_cover(self.opt_output.dir)
|
211
|
-
|
212
|
-
|
258
|
+
if cover_path:
|
259
|
+
thumbnail_bytes: Union[None, bytes, Path] = None
|
260
|
+
cover_ext = Path(cover_path).suffix
|
261
|
+
|
262
|
+
if cover_ext == ".tgs":
|
263
|
+
thumbnail_format = "animated"
|
264
|
+
cover_spec_choice = self.tgs_cover_spec
|
265
|
+
elif cover_ext == ".webm":
|
266
|
+
thumbnail_format = "video"
|
267
|
+
cover_spec_choice = self.webm_cover_spec
|
268
|
+
else:
|
269
|
+
cover_ext = ".png"
|
270
|
+
thumbnail_format = "static"
|
271
|
+
cover_spec_choice = self.png_cover_spec
|
272
|
+
|
213
273
|
if FormatVerify.check_file(cover_path, spec=cover_spec_choice):
|
214
274
|
with open(cover_path, "rb") as f:
|
215
275
|
thumbnail_bytes = f.read()
|
216
276
|
else:
|
217
277
|
_, _, thumbnail_bytes, _ = StickerConvert.convert(
|
218
278
|
cover_path,
|
219
|
-
Path(f"bytes{
|
279
|
+
Path(f"bytes{cover_ext}"),
|
220
280
|
self.opt_comp_cover_merged,
|
221
281
|
self.cb,
|
222
282
|
self.cb_return,
|
223
283
|
)
|
224
284
|
|
225
|
-
if sticker_set is None and sticker_format is not None:
|
226
|
-
if len(input_stickers) > 50:
|
227
|
-
amount_str = "first 50"
|
228
|
-
else:
|
229
|
-
amount_str = "all"
|
230
|
-
start_msg = f"Creating pack and bulk uploading {amount_str} stickers of {pack_short_name}"
|
231
|
-
finish_msg = f"Created pack and bulk uploaded {amount_str} stickers of {pack_short_name}"
|
232
|
-
error_msg = f"Cannot create pack and bulk upload {amount_str} stickers of {pack_short_name} due to"
|
233
|
-
self.cb.put(start_msg)
|
234
|
-
try:
|
235
|
-
await bot.create_new_sticker_set(
|
236
|
-
user_id=self.opt_cred.telegram_userid,
|
237
|
-
name=pack_short_name,
|
238
|
-
title=pack_title,
|
239
|
-
stickers=[a for _, a in input_stickers[:50]],
|
240
|
-
sticker_format=sticker_format,
|
241
|
-
sticker_type=sticker_type,
|
242
|
-
)
|
243
|
-
self.cb.put(finish_msg)
|
244
|
-
input_stickers = input_stickers[50:]
|
245
|
-
except TelegramError as e:
|
246
|
-
self.cb.put(f"{error_msg} {e}")
|
247
|
-
return None
|
248
|
-
|
249
|
-
for src, sticker in input_stickers:
|
250
|
-
try:
|
251
|
-
# We could use tg.start_soon() here
|
252
|
-
# But this would disrupt the order of stickers
|
253
|
-
await bot.add_sticker_to_set(
|
254
|
-
user_id=self.opt_cred.telegram_userid,
|
255
|
-
name=pack_short_name,
|
256
|
-
sticker=sticker,
|
257
|
-
)
|
258
|
-
self.cb.put(f"Uploaded sticker {src} of {pack_short_name}")
|
259
|
-
except BadRequest as e:
|
260
|
-
self.cb.put(
|
261
|
-
f"Cannot upload sticker {src} of {pack_short_name} due to {e}"
|
262
|
-
)
|
263
|
-
if str(e) == "Stickerpack_not_found":
|
264
|
-
self.cb.put(
|
265
|
-
"Hint: You might had deleted and recreated pack too quickly. Wait about 3 minutes and try again."
|
266
|
-
)
|
267
|
-
except TelegramError as e:
|
268
|
-
self.cb.put(
|
269
|
-
f"Cannot upload sticker {src} of {pack_short_name} due to {e}"
|
270
|
-
)
|
271
|
-
|
272
|
-
if thumbnail_bytes is not None:
|
273
285
|
try:
|
274
286
|
self.cb.put(
|
275
287
|
f"Uploading cover (thumbnail) of pack {pack_short_name}"
|
276
288
|
)
|
277
289
|
await bot.set_sticker_set_thumbnail(
|
278
290
|
name=pack_short_name,
|
279
|
-
user_id=self.
|
291
|
+
user_id=self.telegram_userid,
|
280
292
|
thumbnail=thumbnail_bytes,
|
293
|
+
format=thumbnail_format,
|
281
294
|
)
|
282
295
|
self.cb.put(f"Uploaded cover (thumbnail) of pack {pack_short_name}")
|
283
296
|
except TelegramError as e:
|
@@ -300,6 +313,12 @@ class UploadTelegram(UploadBase):
|
|
300
313
|
self.cb.put("Token and userid required for uploading to telegram")
|
301
314
|
return urls
|
302
315
|
|
316
|
+
if self.opt_cred.telegram_userid.isnumeric():
|
317
|
+
self.telegram_userid = int(self.opt_cred.telegram_userid)
|
318
|
+
else:
|
319
|
+
self.cb.put("Invalid userid, should contain numbers only")
|
320
|
+
return urls
|
321
|
+
|
303
322
|
title, _, emoji_dict = MetadataHandler.get_metadata(
|
304
323
|
self.opt_output.dir,
|
305
324
|
title=self.opt_output.title,
|
@@ -327,11 +346,18 @@ class UploadTelegram(UploadBase):
|
|
327
346
|
)
|
328
347
|
|
329
348
|
assert title is not None
|
349
|
+
assert emoji_dict is not None
|
350
|
+
|
351
|
+
if self.opt_output.option == "telegram_emoji":
|
352
|
+
file_per_pack = 200
|
353
|
+
else:
|
354
|
+
file_per_pack = 120
|
355
|
+
|
330
356
|
packs = MetadataHandler.split_sticker_packs(
|
331
357
|
self.opt_output.dir,
|
332
358
|
title=title,
|
333
|
-
file_per_anim_pack=
|
334
|
-
file_per_image_pack=
|
359
|
+
file_per_anim_pack=file_per_pack,
|
360
|
+
file_per_image_pack=file_per_pack,
|
335
361
|
separate_image_anim=not self.opt_comp.fake_vid,
|
336
362
|
)
|
337
363
|
|
@@ -5,41 +5,35 @@ from http.cookiejar import CookieJar
|
|
5
5
|
from typing import Any, Callable, Dict, List, Optional, Union
|
6
6
|
|
7
7
|
import requests
|
8
|
-
import rookiepy
|
8
|
+
import rookiepy
|
9
9
|
|
10
10
|
|
11
11
|
class GetLineAuth:
|
12
12
|
def get_cred(self) -> Optional[str]:
|
13
13
|
browsers: List[Callable[..., Any]] = [
|
14
|
-
rookiepy.load, #
|
15
|
-
rookiepy.firefox,
|
16
|
-
rookiepy.
|
17
|
-
rookiepy.chrome,
|
18
|
-
rookiepy.chromium,
|
19
|
-
rookiepy.brave,
|
20
|
-
rookiepy.edge,
|
21
|
-
rookiepy.opera,
|
22
|
-
rookiepy.vivaldi,
|
14
|
+
rookiepy.load, # Supposed to load from any browser, but may fail
|
15
|
+
rookiepy.firefox,
|
16
|
+
rookiepy.librewolf,
|
17
|
+
rookiepy.chrome,
|
18
|
+
rookiepy.chromium,
|
19
|
+
rookiepy.brave,
|
20
|
+
rookiepy.edge,
|
21
|
+
rookiepy.opera,
|
22
|
+
rookiepy.vivaldi,
|
23
23
|
]
|
24
24
|
|
25
|
-
# https://github.com/thewh1teagle/rookie/pull/24
|
26
|
-
if "libre_wolf" in rookiepy.__all__:
|
27
|
-
browsers.extend(rookiepy.libre_wolf) # type: ignore
|
28
|
-
else:
|
29
|
-
browsers.extend(rookiepy.librewolf) # type: ignore
|
30
|
-
|
31
25
|
if platform.system() == "Windows":
|
32
26
|
browsers.extend(
|
33
27
|
[
|
34
|
-
rookiepy.opera_gx,
|
35
|
-
rookiepy.internet_explorer,
|
28
|
+
rookiepy.opera_gx,
|
29
|
+
rookiepy.internet_explorer,
|
36
30
|
]
|
37
31
|
)
|
38
32
|
elif platform.system() == "Darwin":
|
39
33
|
browsers.extend(
|
40
34
|
[
|
41
|
-
rookiepy.opera_gx,
|
42
|
-
rookiepy.safari,
|
35
|
+
rookiepy.opera_gx,
|
36
|
+
rookiepy.safari,
|
43
37
|
]
|
44
38
|
)
|
45
39
|
|
@@ -48,7 +42,7 @@ class GetLineAuth:
|
|
48
42
|
for browser in browsers:
|
49
43
|
try:
|
50
44
|
cookies_dict = browser(["store.line.me"])
|
51
|
-
cookies_jar = rookiepy.to_cookiejar(cookies_dict)
|
45
|
+
cookies_jar = rookiepy.to_cookiejar(cookies_dict)
|
52
46
|
|
53
47
|
if GetLineAuth.validate_cookies(cookies_jar):
|
54
48
|
break
|
@@ -133,16 +133,21 @@ class MetadataHandler:
|
|
133
133
|
title: Optional[str] = None,
|
134
134
|
author: Optional[str] = None,
|
135
135
|
emoji_dict: Optional[Dict[str, str]] = None,
|
136
|
+
newline: bool = False,
|
136
137
|
) -> None:
|
137
138
|
title_path = Path(directory, "title.txt")
|
138
139
|
if title is not None:
|
139
140
|
with open(title_path, "w+", encoding="utf-8") as f:
|
140
141
|
f.write(title)
|
142
|
+
if newline:
|
143
|
+
f.write("\n")
|
141
144
|
|
142
145
|
author_path = Path(directory, "author.txt")
|
143
146
|
if author is not None:
|
144
147
|
with open(author_path, "w+", encoding="utf-8") as f:
|
145
148
|
f.write(author)
|
149
|
+
if newline:
|
150
|
+
f.write("\n")
|
146
151
|
|
147
152
|
emoji_path = Path(directory, "emoji.txt")
|
148
153
|
if emoji_dict is not None:
|
sticker_convert/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sticker-convert
|
3
|
-
Version: 2.8.
|
3
|
+
Version: 2.8.4
|
4
4
|
Summary: Convert (animated) stickers to/from WhatsApp, Telegram, Signal, Line, Kakao, iMessage. Written in Python.
|
5
5
|
Author-email: laggykiller <chaudominic2@gmail.com>
|
6
6
|
Maintainer-email: laggykiller <chaudominic2@gmail.com>
|
@@ -365,21 +365,21 @@ Requires-Python: >=3.8
|
|
365
365
|
Description-Content-Type: text/markdown
|
366
366
|
License-File: LICENSE
|
367
367
|
Requires-Dist: aiolimiter ~=1.1.0
|
368
|
-
Requires-Dist: anyio ~=3.
|
368
|
+
Requires-Dist: anyio ~=4.3.0
|
369
369
|
Requires-Dist: apngasm-python ~=1.2.3
|
370
370
|
Requires-Dist: av ~=12.0.0
|
371
371
|
Requires-Dist: beautifulsoup4 ~=4.12.3
|
372
|
-
Requires-Dist: rookiepy ~=0.3.
|
372
|
+
Requires-Dist: rookiepy ~=0.3.7
|
373
373
|
Requires-Dist: imagequant ~=1.1.1
|
374
374
|
Requires-Dist: memory-tempfile ~=2.2.3
|
375
375
|
Requires-Dist: mergedeep ~=1.3.4
|
376
376
|
Requires-Dist: numpy >=1.22.4
|
377
|
-
Requires-Dist: Pillow
|
377
|
+
Requires-Dist: Pillow ~=10.3.0
|
378
378
|
Requires-Dist: pyoxipng ~=9.0.0
|
379
|
-
Requires-Dist: python-telegram-bot ~=
|
379
|
+
Requires-Dist: python-telegram-bot ~=21.1
|
380
380
|
Requires-Dist: requests ~=2.31.0
|
381
381
|
Requires-Dist: rlottie-python ~=1.3.4
|
382
|
-
Requires-Dist: signalstickers-client ~=3.3.0
|
382
|
+
Requires-Dist: signalstickers-client-fork-laggykiller ~=3.3.0.post1
|
383
383
|
Requires-Dist: sqlcipher3-wheels ~=0.5.2.post1
|
384
384
|
Requires-Dist: tqdm ~=4.66.2
|
385
385
|
Requires-Dist: ttkbootstrap-fork-laggykiller ~=1.5.1
|
@@ -1,17 +1,17 @@
|
|
1
1
|
sticker_convert/__init__.py,sha256=iQnv6UOOA69c3soAn7ZOnAIubTIQSUxtq1Uhh8xRWvU,102
|
2
2
|
sticker_convert/__main__.py,sha256=6RJauR-SCSSTT3TU7FFB6B6PVwsCxO2xZXtmZ3jc2Is,463
|
3
3
|
sticker_convert/cli.py,sha256=KMl8G25rTpSko46SC-WdI5YDBykyluQL13PYpZW_O8M,18579
|
4
|
-
sticker_convert/converter.py,sha256=
|
4
|
+
sticker_convert/converter.py,sha256=cmUCq_bAsDGYNDBnajxibqcgMrCND1w3icjUTDZSJRA,37353
|
5
5
|
sticker_convert/definitions.py,sha256=ZhP2ALCEud-w9ZZD4c3TDG9eHGPZyaAL7zPUsJAbjtE,2073
|
6
6
|
sticker_convert/gui.py,sha256=TRPGwMhSMPHnZppHmw2OWHKTJtGoeLpGWD0eRYi4_yk,30707
|
7
7
|
sticker_convert/job.py,sha256=dBo98c5dIbg5ZUY8CEE1XQOSoBuf5VOZc-8pmq0Y1Js,25608
|
8
8
|
sticker_convert/job_option.py,sha256=JHAFCxp7-dDwD-1PbpYLAFRF3OoJu8cj_BjOm5r8Gp8,7732
|
9
|
-
sticker_convert/version.py,sha256=
|
9
|
+
sticker_convert/version.py,sha256=c0IeULfsh0KUhBozwZM7ez3i-QW6-94_KuLYYaHKJNM,46
|
10
10
|
sticker_convert/downloaders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
11
|
sticker_convert/downloaders/download_base.py,sha256=CcrgZiBOYJbYcDGCPDHp-ECGXSpfmGtQCzS7KRbBl1E,2726
|
12
12
|
sticker_convert/downloaders/download_kakao.py,sha256=UFp7EpMea62fIePY5DfhH4jThAwdeazfoC5iW1g4dAo,8516
|
13
13
|
sticker_convert/downloaders/download_line.py,sha256=9WzOWujTbZdAqBi52k21OUEfRmcV1loCaJiDmg6dklw,17853
|
14
|
-
sticker_convert/downloaders/download_signal.py,sha256=
|
14
|
+
sticker_convert/downloaders/download_signal.py,sha256=PfwscdbcEd_5C3Ecs0F8Qc8si1sLzLodAdnsHVwXgac,3063
|
15
15
|
sticker_convert/downloaders/download_telegram.py,sha256=jufMqc78aXOPDr7fQf9ykkNyhQ7KVCp4gRBxs09NgMo,4614
|
16
16
|
sticker_convert/gui_components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
17
17
|
sticker_convert/gui_components/gui_utils.py,sha256=okho2cA1Scem_m6rPiYifreFzpFrM21-yUkiAv64EUI,3431
|
@@ -72,29 +72,29 @@ sticker_convert/resources/help.json,sha256=7VKc4Oxw6e4zv2IIeYRQ5e_aa88UlsgIHSBm9
|
|
72
72
|
sticker_convert/resources/input.json,sha256=sRz8qWaLh2KTjjlPIxz2UfynVn2Bn0olywbb8-qT_Hc,2251
|
73
73
|
sticker_convert/resources/output.json,sha256=QYP2gqDvEaAm5I9bH4NReaB1XMLboevv69u-V8YdZUs,1373
|
74
74
|
sticker_convert/uploaders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
75
|
-
sticker_convert/uploaders/compress_wastickers.py,sha256=
|
75
|
+
sticker_convert/uploaders/compress_wastickers.py,sha256=SMPf1_ir30ZKO2ChHspDFuyaufx0XeVBVLOlHmawEdY,6021
|
76
76
|
sticker_convert/uploaders/upload_base.py,sha256=uQupPn6r4zrlAzpKzzX7CgvZb69ATyrwPKahWOQj0ds,1203
|
77
|
-
sticker_convert/uploaders/upload_signal.py,sha256=
|
78
|
-
sticker_convert/uploaders/upload_telegram.py,sha256=
|
77
|
+
sticker_convert/uploaders/upload_signal.py,sha256=eZNfTwnZzPmJcl_UCfJT6rrmLlNpD2EKfxrO2jdi1yg,6396
|
78
|
+
sticker_convert/uploaders/upload_telegram.py,sha256=XnspO9VVbKND0gybUwdFTSzDBVDxLw9O_R7chnVQ0t0,15695
|
79
79
|
sticker_convert/uploaders/xcode_imessage.py,sha256=1gvOljf6kYsq_11tYhnF19Yf4oGY5y34te2DWBRMwf0,11254
|
80
80
|
sticker_convert/utils/callback.py,sha256=_w_dZ3DZTA9m4TjEg6NH_VOa5Dl2YHXdXAlwx_tBdoY,6061
|
81
81
|
sticker_convert/utils/url_detect.py,sha256=Cw3SzHj0xQTgCY8KvXbgFGRn_VhDJuZvH0mWsiQOg5s,769
|
82
82
|
sticker_convert/utils/auth/get_kakao_auth.py,sha256=ipAZ1DUd5CMTpUoxRXHVOFC3DKIpxwxpTYAfrOJ6UZ8,9829
|
83
|
-
sticker_convert/utils/auth/get_line_auth.py,sha256=
|
83
|
+
sticker_convert/utils/auth/get_line_auth.py,sha256=8l8ha2vQmk3rHGvDE7PkcxQXbH3oe62LKbI3qVUtvqc,2196
|
84
84
|
sticker_convert/utils/auth/get_signal_auth.py,sha256=6Sx-lMuyBHeX1RpjAWI8u03qnRu9fmO4p89pd7fowOE,4925
|
85
85
|
sticker_convert/utils/files/cache_store.py,sha256=etfe614OAhAyrnM5fGeESKq6R88YLNqkqkxSzEmZ0V0,1047
|
86
86
|
sticker_convert/utils/files/json_manager.py,sha256=Vr6pZJdLMkrJJWN99210aduVHb0ILyf0SSTaw4TZqgc,541
|
87
87
|
sticker_convert/utils/files/json_resources_loader.py,sha256=flZFixUXRTrOAhvRQpuSQgmJ69yXL94sxukcowLT1JQ,1049
|
88
|
-
sticker_convert/utils/files/metadata_handler.py,sha256=
|
88
|
+
sticker_convert/utils/files/metadata_handler.py,sha256=5ED1UVoAaObgAXe4NZ8sXfsNX7JQvGIqgfCQCjpXRjc,10088
|
89
89
|
sticker_convert/utils/files/run_bin.py,sha256=QalA9je6liHxiOtxsjsFsIkc2t59quhcJCVpP1X3p50,1743
|
90
90
|
sticker_convert/utils/files/sanitize_filename.py,sha256=HBklPGsHRJjFQUIC5rYTQsUrsuTtezZXIEA8CPhLP8A,2156
|
91
91
|
sticker_convert/utils/media/apple_png_normalize.py,sha256=LbrQhc7LlYX4I9ek4XJsZE4l0MygBA1jB-PFiYLEkzk,3657
|
92
92
|
sticker_convert/utils/media/codec_info.py,sha256=SJSFvQzXHnGkj7MH9xJ5xiC4cqiOjFKckFKE_FICdT4,15562
|
93
93
|
sticker_convert/utils/media/decrypt_kakao.py,sha256=4wq9ZDRnFkx1WmFZnyEogBofiLGsWQM_X69HlA36578,1947
|
94
94
|
sticker_convert/utils/media/format_verify.py,sha256=Xf94jyqk_6M9IlFGMy0wYIgQKn_yg00nD4XW0CgAbew,5732
|
95
|
-
sticker_convert-2.8.
|
96
|
-
sticker_convert-2.8.
|
97
|
-
sticker_convert-2.8.
|
98
|
-
sticker_convert-2.8.
|
99
|
-
sticker_convert-2.8.
|
100
|
-
sticker_convert-2.8.
|
95
|
+
sticker_convert-2.8.4.dist-info/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
|
96
|
+
sticker_convert-2.8.4.dist-info/METADATA,sha256=CY_kjWENOtI_N6utqwA3JcVYqf83hd2JGzxHGRdtA8Q,49307
|
97
|
+
sticker_convert-2.8.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
98
|
+
sticker_convert-2.8.4.dist-info/entry_points.txt,sha256=MNJ7XyC--ugxi5jS1nzjDLGnxCyLuaGdsVLnJhDHCqs,66
|
99
|
+
sticker_convert-2.8.4.dist-info/top_level.txt,sha256=r9vfnB0l1ZnH5pTH5RvkobnK3Ow9m0RsncaOMAtiAtk,16
|
100
|
+
sticker_convert-2.8.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|