sticker-convert 2.7.7__py3-none-any.whl → 2.7.9__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/cli.py +4 -0
- sticker_convert/converter.py +90 -37
- sticker_convert/gui.py +12 -2
- sticker_convert/gui_components/frames/comp_frame.py +27 -67
- sticker_convert/gui_components/windows/advanced_compression_window.py +83 -148
- sticker_convert/job.py +12 -4
- sticker_convert/job_option.py +4 -0
- sticker_convert/resources/compression.json +22 -11
- sticker_convert/resources/help.json +2 -1
- sticker_convert/utils/media/codec_info.py +8 -8
- sticker_convert/version.py +1 -1
- {sticker_convert-2.7.7.dist-info → sticker_convert-2.7.9.dist-info}/METADATA +22 -16
- {sticker_convert-2.7.7.dist-info → sticker_convert-2.7.9.dist-info}/RECORD +17 -17
- {sticker_convert-2.7.7.dist-info → sticker_convert-2.7.9.dist-info}/LICENSE +0 -0
- {sticker_convert-2.7.7.dist-info → sticker_convert-2.7.9.dist-info}/WHEEL +0 -0
- {sticker_convert-2.7.7.dist-info → sticker_convert-2.7.9.dist-info}/entry_points.txt +0 -0
- {sticker_convert-2.7.7.dist-info → sticker_convert-2.7.9.dist-info}/top_level.txt +0 -0
sticker_convert/cli.py
CHANGED
@@ -114,6 +114,7 @@ class CLI:
|
|
114
114
|
)
|
115
115
|
flags_comp_float = ("fps_power", "res_power", "quality_power", "color_power")
|
116
116
|
flags_comp_str = (
|
117
|
+
"bg_color",
|
117
118
|
"vid_format",
|
118
119
|
"img_format",
|
119
120
|
"cache_dir",
|
@@ -350,6 +351,9 @@ class CLI:
|
|
350
351
|
duration_max=self.compression_presets[preset]["duration"]["max"]
|
351
352
|
if args.duration_max is None
|
352
353
|
else args.duration_max,
|
354
|
+
bg_color=self.compression_presets[preset]["bg_color"]
|
355
|
+
if args.bg_color is None
|
356
|
+
else args.bg_color,
|
353
357
|
steps=self.compression_presets[preset]["steps"]
|
354
358
|
if args.steps is None
|
355
359
|
else args.steps,
|
sticker_convert/converter.py
CHANGED
@@ -6,10 +6,11 @@ from io import BytesIO
|
|
6
6
|
from math import ceil, floor
|
7
7
|
from pathlib import Path
|
8
8
|
from queue import Queue
|
9
|
-
from typing import TYPE_CHECKING, Any, List, Literal, Optional, Tuple, Union, cast
|
9
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Union, cast
|
10
10
|
|
11
11
|
import numpy as np
|
12
12
|
from PIL import Image
|
13
|
+
from PIL import __version__ as PillowVersion
|
13
14
|
|
14
15
|
from sticker_convert.job_option import CompOption
|
15
16
|
from sticker_convert.utils.callback import Callback, CallbackReturn, CbQueueItemType
|
@@ -378,10 +379,10 @@ class StickerConvert:
|
|
378
379
|
self.frames_raw.append(np.asarray(im.convert("RGBA")))
|
379
380
|
|
380
381
|
def _frames_import_pyav(self) -> None:
|
381
|
-
import av
|
382
|
-
from av.codec.context import CodecContext
|
383
|
-
from av.container.input import InputContainer
|
384
|
-
from av.video.codeccontext import VideoCodecContext
|
382
|
+
import av
|
383
|
+
from av.codec.context import CodecContext
|
384
|
+
from av.container.input import InputContainer
|
385
|
+
from av.video.codeccontext import VideoCodecContext
|
385
386
|
|
386
387
|
# Crashes when handling some webm in yuv420p and convert to rgba
|
387
388
|
# https://github.com/PyAV-Org/PyAV/issues/1166
|
@@ -390,16 +391,17 @@ class StickerConvert:
|
|
390
391
|
file = self.in_f.as_posix()
|
391
392
|
else:
|
392
393
|
file = BytesIO(self.in_f)
|
393
|
-
with av.open(file) as container:
|
394
|
+
with av.open(file) as container:
|
394
395
|
container = cast(InputContainer, container)
|
395
396
|
context = container.streams.video[0].codec_context
|
396
397
|
if context.name == "vp8":
|
397
|
-
context = CodecContext.create("libvpx", "r")
|
398
|
+
context = cast(VideoCodecContext, CodecContext.create("libvpx", "r"))
|
398
399
|
elif context.name == "vp9":
|
399
|
-
context =
|
400
|
-
|
400
|
+
context = cast(
|
401
|
+
VideoCodecContext, CodecContext.create("libvpx-vp9", "r")
|
402
|
+
)
|
401
403
|
|
402
|
-
for packet in container.demux(container.streams.video):
|
404
|
+
for packet in container.demux(container.streams.video):
|
403
405
|
for frame in context.decode(packet):
|
404
406
|
if frame.width % 2 != 0:
|
405
407
|
width = frame.width - 1
|
@@ -410,22 +412,22 @@ class StickerConvert:
|
|
410
412
|
else:
|
411
413
|
height = frame.height
|
412
414
|
if frame.format.name == "yuv420p":
|
413
|
-
rgb_array = frame.to_ndarray(format="rgb24")
|
415
|
+
rgb_array = frame.to_ndarray(format="rgb24")
|
414
416
|
cast("np.ndarray[Any, Any]", rgb_array)
|
415
417
|
rgba_array = np.dstack(
|
416
418
|
(
|
417
419
|
rgb_array,
|
418
420
|
np.zeros(rgb_array.shape[:2], dtype=np.uint8) + 255,
|
419
|
-
)
|
421
|
+
)
|
420
422
|
)
|
421
423
|
else:
|
422
424
|
# yuva420p may cause crash
|
423
425
|
# https://github.com/laggykiller/sticker-convert/issues/114
|
424
|
-
frame = frame.reformat(
|
426
|
+
frame = frame.reformat(
|
425
427
|
width=width,
|
426
428
|
height=height,
|
427
429
|
format="yuva420p",
|
428
|
-
dst_colorspace=1,
|
430
|
+
dst_colorspace=1,
|
429
431
|
)
|
430
432
|
|
431
433
|
# https://stackoverflow.com/questions/72308308/converting-yuv-to-rgb-in-python-coefficients-work-with-array-dont-work-with-n
|
@@ -522,7 +524,27 @@ class StickerConvert:
|
|
522
524
|
elif self.opt_comp.scale_filter == "lanczos":
|
523
525
|
resample = Image.LANCZOS
|
524
526
|
else:
|
525
|
-
resample = Image.
|
527
|
+
resample = Image.BICUBIC
|
528
|
+
|
529
|
+
if not self.opt_comp.bg_color:
|
530
|
+
# Calculate average color of all frames for selecting background color
|
531
|
+
frames_raw_np = np.array(self.frames_raw)
|
532
|
+
s = frames_raw_np.shape
|
533
|
+
colors = frames_raw_np.reshape((-1, s[3])) # type: ignore
|
534
|
+
# Do not count in alpha=0
|
535
|
+
# If alpha > 0, use alpha as weight
|
536
|
+
colors = colors[colors[:, 3] != 0]
|
537
|
+
if colors.shape[0] == 0:
|
538
|
+
bg = (0, 0, 0, 0)
|
539
|
+
else:
|
540
|
+
colors = colors * (colors[3] / 255)
|
541
|
+
if np.mean(colors[:, :3]) < 128:
|
542
|
+
bg = (255, 255, 255, 0)
|
543
|
+
else:
|
544
|
+
bg = (0, 0, 0, 0)
|
545
|
+
else:
|
546
|
+
r, g, b = bytes.fromhex(self.opt_comp.bg_color)
|
547
|
+
bg = (r, g, b, 0)
|
526
548
|
|
527
549
|
for frame in frames_in:
|
528
550
|
with Image.fromarray(frame, "RGBA") as im: # type: ignore
|
@@ -541,10 +563,8 @@ class StickerConvert:
|
|
541
563
|
width_new = width * self.res_h // height
|
542
564
|
|
543
565
|
with im.resize((width_new, height_new), resample=resample) as im_resized:
|
544
|
-
with Image.new(
|
545
|
-
|
546
|
-
) as im_new:
|
547
|
-
im_new.paste(
|
566
|
+
with Image.new("RGBA", (self.res_w, self.res_h), bg) as im_new:
|
567
|
+
im_new.alpha_composite(
|
548
568
|
im_resized,
|
549
569
|
((self.res_w - width_new) // 2, (self.res_h - height_new) // 2),
|
550
570
|
)
|
@@ -615,6 +635,8 @@ class StickerConvert:
|
|
615
635
|
self._frames_export_png()
|
616
636
|
elif self.out_f.suffix == ".webp" and is_animated:
|
617
637
|
self._frames_export_webp()
|
638
|
+
elif self.out_f.suffix == ".gif":
|
639
|
+
self._frames_export_gif()
|
618
640
|
elif self.out_f.suffix in (".webm", ".mp4", ".mkv") or is_animated:
|
619
641
|
self._frames_export_pyav()
|
620
642
|
else:
|
@@ -629,22 +651,17 @@ class StickerConvert:
|
|
629
651
|
)
|
630
652
|
|
631
653
|
def _frames_export_pyav(self) -> None:
|
632
|
-
import av
|
633
|
-
from av.
|
634
|
-
from av.video.stream import VideoStream # type: ignore
|
654
|
+
import av
|
655
|
+
from av.video.stream import VideoStream
|
635
656
|
|
636
|
-
options = {}
|
657
|
+
options: Dict[str, str] = {}
|
637
658
|
|
638
659
|
if isinstance(self.quality, int):
|
639
660
|
# Seems not actually working
|
640
661
|
options["quality"] = str(self.quality)
|
641
662
|
options["lossless"] = "0"
|
642
663
|
|
643
|
-
if self.out_f.suffix
|
644
|
-
codec = "gif"
|
645
|
-
pixel_format = "rgb8"
|
646
|
-
options["loop"] = "0"
|
647
|
-
elif self.out_f.suffix in (".apng", ".png"):
|
664
|
+
if self.out_f.suffix in (".apng", ".png"):
|
648
665
|
codec = "apng"
|
649
666
|
pixel_format = "rgba"
|
650
667
|
options["plays"] = "0"
|
@@ -657,11 +674,10 @@ class StickerConvert:
|
|
657
674
|
pixel_format = "yuv420p"
|
658
675
|
options["loop"] = "0"
|
659
676
|
|
660
|
-
with av.open(
|
677
|
+
with av.open(
|
661
678
|
self.tmp_f, "w", format=self.out_f.suffix.replace(".", "")
|
662
679
|
) as output:
|
663
|
-
|
664
|
-
out_stream = output.add_stream(codec, rate=self.fps, options=options) # type: ignore
|
680
|
+
out_stream = output.add_stream(codec, rate=self.fps, options=options)
|
665
681
|
out_stream = cast(VideoStream, out_stream)
|
666
682
|
assert isinstance(self.res_w, int) and isinstance(self.res_h, int)
|
667
683
|
out_stream.width = self.res_w
|
@@ -669,12 +685,49 @@ class StickerConvert:
|
|
669
685
|
out_stream.pix_fmt = pixel_format
|
670
686
|
|
671
687
|
for frame in self.frames_processed:
|
672
|
-
av_frame = av.VideoFrame.from_ndarray(frame, format="rgba")
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
688
|
+
av_frame = av.VideoFrame.from_ndarray(frame, format="rgba")
|
689
|
+
output.mux(out_stream.encode(av_frame))
|
690
|
+
output.mux(out_stream.encode())
|
691
|
+
|
692
|
+
def _frames_export_gif(self) -> None:
|
693
|
+
extra_kwargs: Dict[str, Any] = {}
|
694
|
+
|
695
|
+
# disposal=2 on gif cause flicker in image with transparency
|
696
|
+
# Occurs in Pillow == 10.2.0
|
697
|
+
# https://github.com/python-pillow/Pillow/issues/7787
|
698
|
+
if PillowVersion == "10.2.0":
|
699
|
+
extra_kwargs["optimize"] = False
|
700
|
+
else:
|
701
|
+
extra_kwargs["optimize"] = True
|
702
|
+
|
703
|
+
# Only enable transparency if all pixels have alpha channel
|
704
|
+
# with value of 0 or 255
|
705
|
+
alpha_channel_values = np.unique(np.array(self.frames_raw)[:, :, :, 3])
|
706
|
+
illegals = np.setxor1d(
|
707
|
+
alpha_channel_values, [0, 255]
|
708
|
+
) # Find all values not 0 or 255
|
709
|
+
if illegals.size == 0:
|
710
|
+
extra_kwargs["transparency"] = 0
|
711
|
+
extra_kwargs["disposal"] = 2
|
712
|
+
im_out = [self.quantize(Image.fromarray(i)) for i in self.frames_processed]
|
713
|
+
else:
|
714
|
+
im_out = [
|
715
|
+
self.quantize(Image.fromarray(i).convert("RGB")).convert("RGB")
|
716
|
+
for i in self.frames_processed
|
717
|
+
]
|
718
|
+
|
719
|
+
if self.fps:
|
720
|
+
extra_kwargs["save_all"] = True
|
721
|
+
extra_kwargs["append_images"] = im_out[1:]
|
722
|
+
extra_kwargs["duration"] = int(1000 / self.fps)
|
723
|
+
extra_kwargs["loop"] = 0
|
724
|
+
|
725
|
+
im_out[0].save(
|
726
|
+
self.tmp_f,
|
727
|
+
format="GIF",
|
728
|
+
quality=self.quality,
|
729
|
+
**extra_kwargs,
|
730
|
+
)
|
678
731
|
|
679
732
|
def _frames_export_webp(self) -> None:
|
680
733
|
import webp # type: ignore
|
sticker_convert/gui.py
CHANGED
@@ -12,6 +12,7 @@ from threading import Thread
|
|
12
12
|
from typing import Any, Callable, Dict, Optional, Union
|
13
13
|
from urllib.parse import urlparse
|
14
14
|
|
15
|
+
from mergedeep import merge # type: ignore
|
15
16
|
from PIL import ImageFont
|
16
17
|
from ttkbootstrap import BooleanVar, DoubleVar, IntVar, StringVar, Toplevel, Window # type: ignore
|
17
18
|
from ttkbootstrap.dialogs import Messagebox, Querybox # type: ignore
|
@@ -127,6 +128,7 @@ class GUI(Window):
|
|
127
128
|
self.img_size_max_var = IntVar(self)
|
128
129
|
self.vid_size_max_var = IntVar(self)
|
129
130
|
self.size_disable_var = BooleanVar()
|
131
|
+
self.bg_color_var = StringVar()
|
130
132
|
self.img_format_var = StringVar(self)
|
131
133
|
self.vid_format_var = StringVar(self)
|
132
134
|
self.fake_vid_var = BooleanVar()
|
@@ -265,7 +267,12 @@ class GUI(Window):
|
|
265
267
|
def save_config(self) -> None:
|
266
268
|
# Only update comp_custom if custom preset is selected
|
267
269
|
if self.comp_preset_var.get() == "custom":
|
268
|
-
comp_custom =
|
270
|
+
comp_custom: Dict[Any, Any] = merge( # type: ignore
|
271
|
+
self.compression_presets.get("custom"), # type: ignore
|
272
|
+
self.get_opt_comp().to_dict(),
|
273
|
+
)
|
274
|
+
comp_custom["format"]["img"] = comp_custom["format"]["img"][0]
|
275
|
+
comp_custom["format"]["vid"] = comp_custom["format"]["vid"][0]
|
269
276
|
del comp_custom["preset"]
|
270
277
|
del comp_custom["no_compress"]
|
271
278
|
else:
|
@@ -332,7 +339,9 @@ class GUI(Window):
|
|
332
339
|
)
|
333
340
|
comp_custom = self.settings.get("comp_custom")
|
334
341
|
if comp_custom:
|
335
|
-
self.compression_presets["custom"] =
|
342
|
+
self.compression_presets["custom"] = merge(
|
343
|
+
self.compression_presets["custom"], comp_custom
|
344
|
+
)
|
336
345
|
self.cache_dir_var.set(self.settings.get("comp", {}).get("cache_dir", ""))
|
337
346
|
self.processes_var.set(
|
338
347
|
self.settings.get("comp", {}).get("processes", ceil(cpu_count() / 2))
|
@@ -509,6 +518,7 @@ class GUI(Window):
|
|
509
518
|
duration_max=self.duration_max_var.get()
|
510
519
|
if not self.duration_disable_var.get()
|
511
520
|
else None,
|
521
|
+
bg_color=self.bg_color_var.get(),
|
512
522
|
steps=self.steps_var.get(),
|
513
523
|
fake_vid=self.fake_vid_var.get(),
|
514
524
|
scale_filter=self.scale_filter_var.get(),
|
@@ -110,73 +110,33 @@ class CompFrame(LabelFrame):
|
|
110
110
|
else:
|
111
111
|
self.gui.no_compress_var.set(False)
|
112
112
|
|
113
|
-
self.gui.
|
114
|
-
self.gui.
|
115
|
-
self.gui.
|
116
|
-
|
117
|
-
)
|
118
|
-
self.gui.
|
119
|
-
|
120
|
-
)
|
121
|
-
self.gui.
|
122
|
-
|
123
|
-
)
|
124
|
-
self.gui.
|
125
|
-
|
126
|
-
)
|
127
|
-
self.gui.
|
128
|
-
|
129
|
-
)
|
130
|
-
self.gui.
|
131
|
-
|
132
|
-
)
|
133
|
-
self.gui.
|
134
|
-
|
135
|
-
)
|
136
|
-
self.gui.
|
137
|
-
|
138
|
-
)
|
139
|
-
self.gui.
|
140
|
-
self.gui.compression_presets[selection]["quality"]["power"]
|
141
|
-
)
|
142
|
-
self.gui.color_min_var.set(
|
143
|
-
self.gui.compression_presets[selection]["color"]["min"]
|
144
|
-
)
|
145
|
-
self.gui.color_max_var.set(
|
146
|
-
self.gui.compression_presets[selection]["color"]["max"]
|
147
|
-
)
|
148
|
-
self.gui.color_power_var.set(
|
149
|
-
self.gui.compression_presets[selection]["color"]["power"]
|
150
|
-
)
|
151
|
-
self.gui.duration_min_var.set(
|
152
|
-
self.gui.compression_presets[selection]["duration"]["min"]
|
153
|
-
)
|
154
|
-
self.gui.duration_max_var.set(
|
155
|
-
self.gui.compression_presets[selection]["duration"]["max"]
|
156
|
-
)
|
157
|
-
self.gui.img_size_max_var.set(
|
158
|
-
self.gui.compression_presets[selection]["size_max"]["img"]
|
159
|
-
)
|
160
|
-
self.gui.vid_size_max_var.set(
|
161
|
-
self.gui.compression_presets[selection]["size_max"]["vid"]
|
162
|
-
)
|
163
|
-
self.gui.img_format_var.set(
|
164
|
-
self.gui.compression_presets[selection]["format"]["img"]
|
165
|
-
)
|
166
|
-
self.gui.vid_format_var.set(
|
167
|
-
self.gui.compression_presets[selection]["format"]["vid"]
|
168
|
-
)
|
169
|
-
self.gui.fake_vid_var.set(self.gui.compression_presets[selection]["fake_vid"])
|
170
|
-
self.gui.scale_filter_var.set(
|
171
|
-
self.gui.compression_presets[selection]["scale_filter"]
|
172
|
-
)
|
173
|
-
self.gui.quantize_method_var.set(
|
174
|
-
self.gui.compression_presets[selection]["quantize_method"]
|
175
|
-
)
|
176
|
-
self.gui.default_emoji_var.set(
|
177
|
-
self.gui.compression_presets[selection]["default_emoji"]
|
178
|
-
)
|
179
|
-
self.gui.steps_var.set(self.gui.compression_presets[selection]["steps"])
|
113
|
+
preset = self.gui.compression_presets[selection]
|
114
|
+
self.gui.fps_min_var.set(preset.get("fps", {}).get("min"))
|
115
|
+
self.gui.fps_max_var.set(preset.get("fps", {}).get("max"))
|
116
|
+
self.gui.fps_power_var.set(preset.get("fps", {}).get("power"))
|
117
|
+
self.gui.res_w_min_var.set(preset.get("res", {}).get("w", {}).get("min"))
|
118
|
+
self.gui.res_w_max_var.set(preset.get("res", {}).get("w", {}).get("max"))
|
119
|
+
self.gui.res_h_min_var.set(preset.get("res", {}).get("h", {}).get("min"))
|
120
|
+
self.gui.res_h_max_var.set(preset.get("res", {}).get("h", {}).get("max"))
|
121
|
+
self.gui.res_power_var.set(preset.get("res", {}).get("power"))
|
122
|
+
self.gui.quality_min_var.set(preset.get("quality", {}).get("min"))
|
123
|
+
self.gui.quality_max_var.set(preset.get("quality", {}).get("max"))
|
124
|
+
self.gui.quality_power_var.set(preset.get("quality", {}).get("power"))
|
125
|
+
self.gui.color_min_var.set(preset.get("color", {}).get("min"))
|
126
|
+
self.gui.color_max_var.set(preset.get("color", {}).get("max"))
|
127
|
+
self.gui.color_power_var.set(preset.get("color", {}).get("power"))
|
128
|
+
self.gui.duration_min_var.set(preset.get("duration", {}).get("min"))
|
129
|
+
self.gui.duration_max_var.set(preset.get("duration", {}).get("max"))
|
130
|
+
self.gui.img_size_max_var.set(preset.get("size_max", {}).get("img"))
|
131
|
+
self.gui.vid_size_max_var.set(preset.get("size_max", {}).get("vid"))
|
132
|
+
self.gui.img_format_var.set(preset.get("format", {}).get("img"))
|
133
|
+
self.gui.vid_format_var.set(preset.get("format", {}).get("vid"))
|
134
|
+
self.gui.bg_color_var.set(preset.get("bg_color"))
|
135
|
+
self.gui.fake_vid_var.set(preset.get("fake_vid"))
|
136
|
+
self.gui.scale_filter_var.set(preset.get("scale_filter"))
|
137
|
+
self.gui.quantize_method_var.set(preset.get("quantize_method"))
|
138
|
+
self.gui.default_emoji_var.set(preset.get("default_emoji"))
|
139
|
+
self.gui.steps_var.set(preset.get("steps"))
|
180
140
|
|
181
141
|
self.cb_no_compress()
|
182
142
|
self.gui.highlight_fields()
|
@@ -2,6 +2,7 @@
|
|
2
2
|
from __future__ import annotations
|
3
3
|
|
4
4
|
from functools import partial
|
5
|
+
from tkinter import colorchooser
|
5
6
|
from typing import Any, List, Tuple
|
6
7
|
|
7
8
|
from PIL import Image, ImageDraw, ImageTk
|
@@ -13,7 +14,7 @@ from sticker_convert.gui_components.windows.base_window import BaseWindow
|
|
13
14
|
|
14
15
|
|
15
16
|
class AdvancedCompressionWindow(BaseWindow):
|
16
|
-
emoji_column_per_row =
|
17
|
+
emoji_column_per_row = 8
|
17
18
|
emoji_visible_rows = 5
|
18
19
|
emoji_btns: List[Tuple[Button, ImageTk.PhotoImage]] = []
|
19
20
|
|
@@ -33,15 +34,7 @@ class AdvancedCompressionWindow(BaseWindow):
|
|
33
34
|
|
34
35
|
self.frame_advcomp.grid_columnconfigure(6, weight=1)
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
self.fps_help_btn = Button(
|
39
|
-
self.frame_advcomp,
|
40
|
-
text="?",
|
41
|
-
width=1,
|
42
|
-
command=lambda: cb_msg_block_adv_comp_win(self.gui.help["comp"]["fps"]),
|
43
|
-
bootstyle="secondary", # type: ignore
|
44
|
-
)
|
37
|
+
self.fps_help_btn = self.add_help_btn(self.gui.help["comp"]["fps"])
|
45
38
|
self.fps_lbl = Label(self.frame_advcomp, text="Output FPS")
|
46
39
|
self.fps_min_lbl = Label(self.frame_advcomp, text="Min:")
|
47
40
|
self.fps_min_entry = Entry(
|
@@ -63,13 +56,7 @@ class AdvancedCompressionWindow(BaseWindow):
|
|
63
56
|
bootstyle="danger-round-toggle", # type: ignore
|
64
57
|
)
|
65
58
|
|
66
|
-
self.res_w_help_btn =
|
67
|
-
self.frame_advcomp,
|
68
|
-
text="?",
|
69
|
-
width=1,
|
70
|
-
command=lambda: cb_msg_block_adv_comp_win(self.gui.help["comp"]["res"]),
|
71
|
-
bootstyle="secondary", # type: ignore
|
72
|
-
)
|
59
|
+
self.res_w_help_btn = self.add_help_btn(self.gui.help["comp"]["res"])
|
73
60
|
self.res_w_lbl = Label(self.frame_advcomp, text="Output resolution (Width)")
|
74
61
|
self.res_w_min_lbl = Label(self.frame_advcomp, text="Min:")
|
75
62
|
self.res_w_min_entry = Entry(
|
@@ -91,13 +78,7 @@ class AdvancedCompressionWindow(BaseWindow):
|
|
91
78
|
bootstyle="danger-round-toggle", # type: ignore
|
92
79
|
)
|
93
80
|
|
94
|
-
self.res_h_help_btn =
|
95
|
-
self.frame_advcomp,
|
96
|
-
text="?",
|
97
|
-
width=1,
|
98
|
-
command=lambda: cb_msg_block_adv_comp_win(self.gui.help["comp"]["res"]),
|
99
|
-
bootstyle="secondary", # type: ignore
|
100
|
-
)
|
81
|
+
self.res_h_help_btn = self.add_help_btn(self.gui.help["comp"]["res"])
|
101
82
|
self.res_h_lbl = Label(self.frame_advcomp, text="Output resolution (Height)")
|
102
83
|
self.res_h_min_lbl = Label(self.frame_advcomp, text="Min:")
|
103
84
|
self.res_h_min_entry = Entry(
|
@@ -119,13 +100,7 @@ class AdvancedCompressionWindow(BaseWindow):
|
|
119
100
|
bootstyle="danger-round-toggle", # type: ignore
|
120
101
|
)
|
121
102
|
|
122
|
-
self.quality_help_btn =
|
123
|
-
self.frame_advcomp,
|
124
|
-
text="?",
|
125
|
-
width=1,
|
126
|
-
command=lambda: cb_msg_block_adv_comp_win(self.gui.help["comp"]["quality"]),
|
127
|
-
bootstyle="secondary", # type: ignore
|
128
|
-
)
|
103
|
+
self.quality_help_btn = self.add_help_btn(self.gui.help["comp"]["quality"])
|
129
104
|
self.quality_lbl = Label(self.frame_advcomp, text="Output quality (0-100)")
|
130
105
|
self.quality_min_lbl = Label(self.frame_advcomp, text="Min:")
|
131
106
|
self.quality_min_entry = Entry(
|
@@ -147,13 +122,7 @@ class AdvancedCompressionWindow(BaseWindow):
|
|
147
122
|
bootstyle="danger-round-toggle", # type: ignore
|
148
123
|
)
|
149
124
|
|
150
|
-
self.color_help_btn =
|
151
|
-
self.frame_advcomp,
|
152
|
-
text="?",
|
153
|
-
width=1,
|
154
|
-
command=lambda: cb_msg_block_adv_comp_win(self.gui.help["comp"]["color"]),
|
155
|
-
bootstyle="secondary", # type: ignore
|
156
|
-
)
|
125
|
+
self.color_help_btn = self.add_help_btn(self.gui.help["comp"]["color"])
|
157
126
|
self.color_lbl = Label(self.frame_advcomp, text="Colors (0-256)")
|
158
127
|
self.color_min_lbl = Label(self.frame_advcomp, text="Min:")
|
159
128
|
self.color_min_entry = Entry(
|
@@ -175,15 +144,7 @@ class AdvancedCompressionWindow(BaseWindow):
|
|
175
144
|
bootstyle="danger-round-toggle", # type: ignore
|
176
145
|
)
|
177
146
|
|
178
|
-
self.duration_help_btn =
|
179
|
-
self.frame_advcomp,
|
180
|
-
text="?",
|
181
|
-
width=1,
|
182
|
-
command=lambda: cb_msg_block_adv_comp_win(
|
183
|
-
self.gui.help["comp"]["duration"]
|
184
|
-
),
|
185
|
-
bootstyle="secondary", # type: ignore
|
186
|
-
)
|
147
|
+
self.duration_help_btn = self.add_help_btn(self.gui.help["comp"]["duration"])
|
187
148
|
self.duration_lbl = Label(self.frame_advcomp, text="Duration (Miliseconds)")
|
188
149
|
self.duration_min_lbl = Label(self.frame_advcomp, text="Min:")
|
189
150
|
self.duration_min_entry = Entry(
|
@@ -205,13 +166,7 @@ class AdvancedCompressionWindow(BaseWindow):
|
|
205
166
|
bootstyle="danger-round-toggle", # type: ignore
|
206
167
|
)
|
207
168
|
|
208
|
-
self.size_help_btn =
|
209
|
-
self.frame_advcomp,
|
210
|
-
text="?",
|
211
|
-
width=1,
|
212
|
-
command=lambda: cb_msg_block_adv_comp_win(self.gui.help["comp"]["size"]),
|
213
|
-
bootstyle="secondary", # type: ignore
|
214
|
-
)
|
169
|
+
self.size_help_btn = self.add_help_btn(self.gui.help["comp"]["size"])
|
215
170
|
self.size_lbl = Label(self.frame_advcomp, text="Maximum file size (bytes)")
|
216
171
|
self.img_size_max_lbl = Label(self.frame_advcomp, text="Img:")
|
217
172
|
self.img_size_max_entry = Entry(
|
@@ -233,13 +188,7 @@ class AdvancedCompressionWindow(BaseWindow):
|
|
233
188
|
bootstyle="danger-round-toggle", # type: ignore
|
234
189
|
)
|
235
190
|
|
236
|
-
self.format_help_btn =
|
237
|
-
self.frame_advcomp,
|
238
|
-
text="?",
|
239
|
-
width=1,
|
240
|
-
command=lambda: cb_msg_block_adv_comp_win(self.gui.help["comp"]["format"]),
|
241
|
-
bootstyle="secondary", # type: ignore
|
242
|
-
)
|
191
|
+
self.format_help_btn = self.add_help_btn(self.gui.help["comp"]["format"])
|
243
192
|
self.format_lbl = Label(self.frame_advcomp, text="File format")
|
244
193
|
self.img_format_lbl = Label(self.frame_advcomp, text="Img:")
|
245
194
|
self.img_format_entry = Entry(
|
@@ -252,15 +201,7 @@ class AdvancedCompressionWindow(BaseWindow):
|
|
252
201
|
)
|
253
202
|
self.vid_format_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
|
254
203
|
|
255
|
-
self.power_help_btn1 =
|
256
|
-
self.frame_advcomp,
|
257
|
-
text="?",
|
258
|
-
width=1,
|
259
|
-
command=lambda: cb_msg_block_adv_comp_win(
|
260
|
-
self.gui.help["comp"]["fps_power"]
|
261
|
-
),
|
262
|
-
bootstyle="secondary", # type: ignore
|
263
|
-
)
|
204
|
+
self.power_help_btn1 = self.add_help_btn(self.gui.help["comp"]["fps_power"])
|
264
205
|
self.power_lbl1 = Label(self.frame_advcomp, text="Power (Importance)")
|
265
206
|
self.fps_power_lbl = Label(self.frame_advcomp, text="FPS:")
|
266
207
|
self.fps_power_entry = Entry(
|
@@ -273,15 +214,7 @@ class AdvancedCompressionWindow(BaseWindow):
|
|
273
214
|
)
|
274
215
|
self.res_power_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
|
275
216
|
|
276
|
-
self.power_help_btn2 =
|
277
|
-
self.frame_advcomp,
|
278
|
-
text="?",
|
279
|
-
width=1,
|
280
|
-
command=lambda: cb_msg_block_adv_comp_win(
|
281
|
-
self.gui.help["comp"]["fps_power"]
|
282
|
-
),
|
283
|
-
bootstyle="secondary", # type: ignore
|
284
|
-
)
|
217
|
+
self.power_help_btn2 = self.add_help_btn(self.gui.help["comp"]["fps_power"])
|
285
218
|
self.power_lbl2 = Label(self.frame_advcomp, text="Power (Importance)")
|
286
219
|
self.quality_power_lbl = Label(self.frame_advcomp, text="Quality:")
|
287
220
|
self.quality_power_entry = Entry(
|
@@ -294,15 +227,7 @@ class AdvancedCompressionWindow(BaseWindow):
|
|
294
227
|
)
|
295
228
|
self.color_power_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
|
296
229
|
|
297
|
-
self.fake_vid_help_btn =
|
298
|
-
self.frame_advcomp,
|
299
|
-
text="?",
|
300
|
-
width=1,
|
301
|
-
command=lambda: cb_msg_block_adv_comp_win(
|
302
|
-
self.gui.help["comp"]["fake_vid"]
|
303
|
-
),
|
304
|
-
bootstyle="secondary", # type: ignore
|
305
|
-
)
|
230
|
+
self.fake_vid_help_btn = self.add_help_btn(self.gui.help["comp"]["fake_vid"])
|
306
231
|
self.fake_vid_lbl = Label(
|
307
232
|
self.frame_advcomp, text="Convert (faking) image to video"
|
308
233
|
)
|
@@ -314,15 +239,21 @@ class AdvancedCompressionWindow(BaseWindow):
|
|
314
239
|
bootstyle="success-round-toggle", # type: ignore
|
315
240
|
)
|
316
241
|
|
317
|
-
self.
|
242
|
+
self.bg_color_help_btn = self.add_help_btn(self.gui.help["comp"]["bg_color"])
|
243
|
+
self.bg_color_lbl = Label(self.frame_advcomp, text="Background color")
|
244
|
+
self.bg_color_entry = Entry(
|
245
|
+
self.frame_advcomp, textvariable=self.gui.bg_color_var, width=8
|
246
|
+
)
|
247
|
+
self.bg_color_btn = Button(
|
318
248
|
self.frame_advcomp,
|
319
|
-
text="
|
320
|
-
|
321
|
-
command=lambda: cb_msg_block_adv_comp_win(
|
322
|
-
self.gui.help["comp"]["scale_filter"]
|
323
|
-
),
|
249
|
+
text="Set",
|
250
|
+
command=self.cb_bg_color,
|
324
251
|
bootstyle="secondary", # type: ignore
|
325
252
|
)
|
253
|
+
|
254
|
+
self.scale_filter_help_btn = self.add_help_btn(
|
255
|
+
self.gui.help["comp"]["scale_filter"]
|
256
|
+
)
|
326
257
|
self.scale_filter_lbl = Label(self.frame_advcomp, text="Scale filter")
|
327
258
|
self.scale_filter_opt = OptionMenu(
|
328
259
|
self.frame_advcomp,
|
@@ -337,14 +268,8 @@ class AdvancedCompressionWindow(BaseWindow):
|
|
337
268
|
bootstyle="secondary", # type: ignore
|
338
269
|
)
|
339
270
|
|
340
|
-
self.quantize_method_help_btn =
|
341
|
-
self.
|
342
|
-
text="?",
|
343
|
-
width=1,
|
344
|
-
command=lambda: cb_msg_block_adv_comp_win(
|
345
|
-
self.gui.help["comp"]["quantize_method"]
|
346
|
-
),
|
347
|
-
bootstyle="secondary", # type: ignore
|
271
|
+
self.quantize_method_help_btn = self.add_help_btn(
|
272
|
+
self.gui.help["comp"]["quantize_method"]
|
348
273
|
)
|
349
274
|
self.quantize_method_lbl = Label(self.frame_advcomp, text="Quantize method")
|
350
275
|
self.quantize_method_opt = OptionMenu(
|
@@ -357,29 +282,15 @@ class AdvancedCompressionWindow(BaseWindow):
|
|
357
282
|
bootstyle="secondary", # type: ignore
|
358
283
|
)
|
359
284
|
|
360
|
-
self.cache_dir_help_btn =
|
361
|
-
self.frame_advcomp,
|
362
|
-
text="?",
|
363
|
-
width=1,
|
364
|
-
command=lambda: cb_msg_block_adv_comp_win(
|
365
|
-
self.gui.help["comp"]["cache_dir"]
|
366
|
-
),
|
367
|
-
bootstyle="secondary", # type: ignore
|
368
|
-
)
|
285
|
+
self.cache_dir_help_btn = self.add_help_btn(self.gui.help["comp"]["cache_dir"])
|
369
286
|
self.cache_dir_lbl = Label(self.frame_advcomp, text="Custom cache directory")
|
370
287
|
self.cache_dir_entry = Entry(
|
371
288
|
self.frame_advcomp, textvariable=self.gui.cache_dir_var, width=30
|
372
289
|
)
|
373
290
|
self.cache_dir_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
|
374
291
|
|
375
|
-
self.default_emoji_help_btn =
|
376
|
-
self.
|
377
|
-
text="?",
|
378
|
-
width=1,
|
379
|
-
command=lambda: cb_msg_block_adv_comp_win(
|
380
|
-
self.gui.help["comp"]["default_emoji"]
|
381
|
-
),
|
382
|
-
bootstyle="secondary", # type: ignore
|
292
|
+
self.default_emoji_help_btn = self.add_help_btn(
|
293
|
+
self.gui.help["comp"]["default_emoji"]
|
383
294
|
)
|
384
295
|
self.default_emoji_lbl = Label(self.frame_advcomp, text="Default emoji")
|
385
296
|
self.im: Image.Image = Image.new("RGBA", (32, 32), (255, 255, 255, 0))
|
@@ -467,27 +378,32 @@ class AdvancedCompressionWindow(BaseWindow):
|
|
467
378
|
self.fake_vid_lbl.grid(column=1, row=12, sticky="w", padx=3, pady=3)
|
468
379
|
self.fake_vid_cbox.grid(column=6, row=12, sticky="nes", padx=3, pady=3)
|
469
380
|
|
470
|
-
self.
|
471
|
-
self.
|
381
|
+
self.bg_color_help_btn.grid(column=0, row=13, sticky="w", padx=3, pady=3)
|
382
|
+
self.bg_color_lbl.grid(column=1, row=13, sticky="w", padx=3, pady=3)
|
383
|
+
self.bg_color_entry.grid(column=5, row=13, sticky="w", padx=3, pady=3)
|
384
|
+
self.bg_color_btn.grid(column=6, row=13, sticky="nes", padx=3, pady=3)
|
385
|
+
|
386
|
+
self.scale_filter_help_btn.grid(column=0, row=14, sticky="w", padx=3, pady=3)
|
387
|
+
self.scale_filter_lbl.grid(column=1, row=14, sticky="w", padx=3, pady=3)
|
472
388
|
self.scale_filter_opt.grid(
|
473
|
-
column=2, row=
|
389
|
+
column=2, row=14, columnspan=4, sticky="nes", padx=3, pady=3
|
474
390
|
)
|
475
391
|
|
476
|
-
self.quantize_method_help_btn.grid(column=0, row=
|
477
|
-
self.quantize_method_lbl.grid(column=1, row=
|
392
|
+
self.quantize_method_help_btn.grid(column=0, row=15, sticky="w", padx=3, pady=3)
|
393
|
+
self.quantize_method_lbl.grid(column=1, row=15, sticky="w", padx=3, pady=3)
|
478
394
|
self.quantize_method_opt.grid(
|
479
|
-
column=2, row=
|
395
|
+
column=2, row=15, columnspan=4, sticky="nes", padx=3, pady=3
|
480
396
|
)
|
481
397
|
|
482
|
-
self.cache_dir_help_btn.grid(column=0, row=
|
483
|
-
self.cache_dir_lbl.grid(column=1, row=
|
398
|
+
self.cache_dir_help_btn.grid(column=0, row=16, sticky="w", padx=3, pady=3)
|
399
|
+
self.cache_dir_lbl.grid(column=1, row=16, sticky="w", padx=3, pady=3)
|
484
400
|
self.cache_dir_entry.grid(
|
485
|
-
column=2, row=
|
401
|
+
column=2, row=16, columnspan=4, sticky="nes", padx=3, pady=3
|
486
402
|
)
|
487
403
|
|
488
|
-
self.default_emoji_help_btn.grid(column=0, row=
|
489
|
-
self.default_emoji_lbl.grid(column=1, row=
|
490
|
-
self.default_emoji_dsp.grid(column=6, row=
|
404
|
+
self.default_emoji_help_btn.grid(column=0, row=17, sticky="w", padx=3, pady=3)
|
405
|
+
self.default_emoji_lbl.grid(column=1, row=17, sticky="w", padx=3, pady=3)
|
406
|
+
self.default_emoji_dsp.grid(column=6, row=17, sticky="nes", padx=3, pady=3)
|
491
407
|
|
492
408
|
# https://stackoverflow.com/questions/43731784/tkinter-canvas-scrollbar-with-grid
|
493
409
|
# Create a frame for the canvas with non-zero row&column weights
|
@@ -647,6 +563,42 @@ class AdvancedCompressionWindow(BaseWindow):
|
|
647
563
|
|
648
564
|
self.fake_vid_cbox.config(state=state)
|
649
565
|
|
566
|
+
def cb_bg_color(self, *_: Any) -> None:
|
567
|
+
color = colorchooser.askcolor(title="Choose color")[1]
|
568
|
+
if color:
|
569
|
+
self.gui.bg_color_var.set(color.replace("#", ""))
|
570
|
+
self.lift()
|
571
|
+
self.attributes("-topmost", True) # type: ignore
|
572
|
+
self.attributes("-topmost", False) # type: ignore
|
573
|
+
|
574
|
+
def cb_bound_to_mousewheel(self, event: Any) -> None:
|
575
|
+
for i in self.mousewheel:
|
576
|
+
self.emoji_canvas.bind_all(i, self.cb_on_mousewheel)
|
577
|
+
|
578
|
+
def cb_unbound_to_mousewheel(self, event: Any) -> None:
|
579
|
+
for i in self.mousewheel:
|
580
|
+
self.emoji_canvas.unbind_all(i)
|
581
|
+
|
582
|
+
def cb_on_mousewheel(self, event: Any) -> None:
|
583
|
+
self.emoji_canvas.yview_scroll(
|
584
|
+
int(-1 * (event.delta / self.delta_divide)), "units"
|
585
|
+
) # type: ignore
|
586
|
+
|
587
|
+
def cb_set_emoji(self, emoji: str) -> None:
|
588
|
+
self.gui.default_emoji_var.set(emoji)
|
589
|
+
self.set_emoji_btn()
|
590
|
+
|
591
|
+
def add_help_btn(self, msg: str) -> Button:
|
592
|
+
cb_msg_block_adv_comp_win = partial(self.gui.cb_msg_block, parent=self)
|
593
|
+
|
594
|
+
return Button(
|
595
|
+
self.frame_advcomp,
|
596
|
+
text="?",
|
597
|
+
width=1,
|
598
|
+
command=lambda: cb_msg_block_adv_comp_win(msg),
|
599
|
+
bootstyle="secondary", # type: ignore
|
600
|
+
)
|
601
|
+
|
650
602
|
def set_emoji_btn(self) -> None:
|
651
603
|
self.im = Image.new("RGBA", (128, 128), (255, 255, 255, 0))
|
652
604
|
ImageDraw.Draw(self.im).text( # type: ignore
|
@@ -736,20 +688,3 @@ class AdvancedCompressionWindow(BaseWindow):
|
|
736
688
|
# https://stackoverflow.com/questions/17355902/tkinter-binding-mousewheel-to-scrollbar
|
737
689
|
self.emoji_canvas.bind("<Enter>", self.cb_bound_to_mousewheel)
|
738
690
|
self.emoji_canvas.bind("<Leave>", self.cb_unbound_to_mousewheel)
|
739
|
-
|
740
|
-
def cb_bound_to_mousewheel(self, event: Any) -> None:
|
741
|
-
for i in self.mousewheel:
|
742
|
-
self.emoji_canvas.bind_all(i, self.cb_on_mousewheel)
|
743
|
-
|
744
|
-
def cb_unbound_to_mousewheel(self, event: Any) -> None:
|
745
|
-
for i in self.mousewheel:
|
746
|
-
self.emoji_canvas.unbind_all(i)
|
747
|
-
|
748
|
-
def cb_on_mousewheel(self, event: Any) -> None:
|
749
|
-
self.emoji_canvas.yview_scroll(
|
750
|
-
int(-1 * (event.delta / self.delta_divide)), "units"
|
751
|
-
) # type: ignore
|
752
|
-
|
753
|
-
def cb_set_emoji(self, emoji: str) -> None:
|
754
|
-
self.gui.default_emoji_var.set(emoji)
|
755
|
-
self.set_emoji_btn()
|
sticker_convert/job.py
CHANGED
@@ -29,7 +29,6 @@ from sticker_convert.utils.files.json_resources_loader import OUTPUT_JSON
|
|
29
29
|
from sticker_convert.utils.files.metadata_handler import MetadataHandler
|
30
30
|
from sticker_convert.utils.media.codec_info import CodecInfo
|
31
31
|
|
32
|
-
CbQueueType = Queue[CbQueueItemType]
|
33
32
|
WorkListItemType = Optional[Tuple[Callable[..., Any], Tuple[Any, ...]]]
|
34
33
|
if TYPE_CHECKING:
|
35
34
|
# mypy complains about this
|
@@ -59,7 +58,7 @@ class Executor:
|
|
59
58
|
# Especially when using scale_filter=nearest
|
60
59
|
self.work_list: WorkListType = self.manager.list()
|
61
60
|
self.results_queue: Queue[Any] = self.manager.Queue()
|
62
|
-
self.cb_queue:
|
61
|
+
self.cb_queue: Queue[CbQueueItemType] = self.manager.Queue()
|
63
62
|
self.cb_return = CallbackReturn()
|
64
63
|
self.processes: List[Process] = []
|
65
64
|
|
@@ -76,7 +75,7 @@ class Executor:
|
|
76
75
|
|
77
76
|
def cb_thread(
|
78
77
|
self,
|
79
|
-
cb_queue:
|
78
|
+
cb_queue: Queue[CbQueueItemType],
|
80
79
|
cb_return: CallbackReturn,
|
81
80
|
) -> None:
|
82
81
|
for i in iter(cb_queue.get, None):
|
@@ -113,7 +112,7 @@ class Executor:
|
|
113
112
|
def worker(
|
114
113
|
work_list: WorkListType,
|
115
114
|
results_queue: Queue[Any],
|
116
|
-
cb_queue:
|
115
|
+
cb_queue: Queue[CbQueueItemType],
|
117
116
|
cb_return: CallbackReturn,
|
118
117
|
) -> None:
|
119
118
|
while True:
|
@@ -412,6 +411,15 @@ class Job:
|
|
412
411
|
error_msg += f"[X] quantize_method {self.opt_comp.quantize_method} is not valid option"
|
413
412
|
error_msg += " Valid options: imagequant, fastoctree, none"
|
414
413
|
|
414
|
+
if self.opt_comp.bg_color:
|
415
|
+
try:
|
416
|
+
_, _, _ = bytes.fromhex(self.opt_comp.bg_color)
|
417
|
+
except ValueError:
|
418
|
+
error_msg += "\n"
|
419
|
+
error_msg += (
|
420
|
+
f"[X] bg_color {self.opt_comp.bg_color} is not valid color hex"
|
421
|
+
)
|
422
|
+
|
415
423
|
# Warn about unable to download animated Kakao stickers with such link
|
416
424
|
if (
|
417
425
|
self.opt_output.option == "kakao"
|
sticker_convert/job_option.py
CHANGED
@@ -67,6 +67,8 @@ class CompOption(BaseOption):
|
|
67
67
|
duration_min: Optional[int] = None
|
68
68
|
duration_max: Optional[int] = None
|
69
69
|
|
70
|
+
bg_color: Optional[str] = None
|
71
|
+
|
70
72
|
steps: int = 1
|
71
73
|
fake_vid: Optional[bool] = None
|
72
74
|
quantize_method: Optional[str] = None
|
@@ -101,7 +103,9 @@ class CompOption(BaseOption):
|
|
101
103
|
},
|
102
104
|
"duration": {"min": self.duration_min, "max": self.duration_max},
|
103
105
|
"steps": self.steps,
|
106
|
+
"bg_color": self.bg_color,
|
104
107
|
"fake_vid": self.fake_vid,
|
108
|
+
"quantize_method": self.quantize_method,
|
105
109
|
"scale_filter": self.scale_filter,
|
106
110
|
"cache_dir": self.cache_dir,
|
107
111
|
"default_emoji": self.default_emoji,
|
@@ -38,9 +38,10 @@
|
|
38
38
|
"min": 0,
|
39
39
|
"max": 10000
|
40
40
|
},
|
41
|
+
"bg_color": "",
|
41
42
|
"steps": 16,
|
42
43
|
"fake_vid": false,
|
43
|
-
"scale_filter": "
|
44
|
+
"scale_filter": "bicubic",
|
44
45
|
"quantize_method": "imagequant",
|
45
46
|
"default_emoji": "😀"
|
46
47
|
},
|
@@ -83,9 +84,10 @@
|
|
83
84
|
"min": 0,
|
84
85
|
"max": 3000
|
85
86
|
},
|
87
|
+
"bg_color": "",
|
86
88
|
"steps": 16,
|
87
89
|
"fake_vid": false,
|
88
|
-
"scale_filter": "
|
90
|
+
"scale_filter": "bicubic",
|
89
91
|
"quantize_method": "imagequant",
|
90
92
|
"default_emoji": "😀"
|
91
93
|
},
|
@@ -128,9 +130,10 @@
|
|
128
130
|
"min": 0,
|
129
131
|
"max": 3000
|
130
132
|
},
|
133
|
+
"bg_color": "",
|
131
134
|
"steps": 16,
|
132
135
|
"fake_vid": false,
|
133
|
-
"scale_filter": "
|
136
|
+
"scale_filter": "bicubic",
|
134
137
|
"quantize_method": "imagequant",
|
135
138
|
"default_emoji": "😀"
|
136
139
|
},
|
@@ -173,9 +176,10 @@
|
|
173
176
|
"min": 0,
|
174
177
|
"max": 3000
|
175
178
|
},
|
179
|
+
"bg_color": "",
|
176
180
|
"steps": 16,
|
177
181
|
"fake_vid": false,
|
178
|
-
"scale_filter": "
|
182
|
+
"scale_filter": "bicubic",
|
179
183
|
"quantize_method": "imagequant",
|
180
184
|
"default_emoji": "😀"
|
181
185
|
},
|
@@ -218,9 +222,10 @@
|
|
218
222
|
"min": 8,
|
219
223
|
"max": 10000
|
220
224
|
},
|
225
|
+
"bg_color": "",
|
221
226
|
"steps": 16,
|
222
227
|
"fake_vid": true,
|
223
|
-
"scale_filter": "
|
228
|
+
"scale_filter": "bicubic",
|
224
229
|
"quantize_method": "imagequant",
|
225
230
|
"default_emoji": "😀"
|
226
231
|
},
|
@@ -263,9 +268,10 @@
|
|
263
268
|
"min": 83,
|
264
269
|
"max": 4000
|
265
270
|
},
|
271
|
+
"bg_color": "",
|
266
272
|
"steps": 16,
|
267
273
|
"fake_vid": false,
|
268
|
-
"scale_filter": "
|
274
|
+
"scale_filter": "bicubic",
|
269
275
|
"quantize_method": "imagequant",
|
270
276
|
"default_emoji": "😀"
|
271
277
|
},
|
@@ -308,9 +314,10 @@
|
|
308
314
|
"min": 0,
|
309
315
|
"max": 5000
|
310
316
|
},
|
317
|
+
"bg_color": "",
|
311
318
|
"steps": 16,
|
312
319
|
"fake_vid": false,
|
313
|
-
"scale_filter": "
|
320
|
+
"scale_filter": "bicubic",
|
314
321
|
"quantize_method": "imagequant",
|
315
322
|
"default_emoji": "😀"
|
316
323
|
},
|
@@ -353,9 +360,10 @@
|
|
353
360
|
"min": 0,
|
354
361
|
"max": 2000
|
355
362
|
},
|
363
|
+
"bg_color": "",
|
356
364
|
"steps": 16,
|
357
365
|
"fake_vid": false,
|
358
|
-
"scale_filter": "
|
366
|
+
"scale_filter": "bicubic",
|
359
367
|
"quantize_method": "imagequant",
|
360
368
|
"default_emoji": "😀"
|
361
369
|
},
|
@@ -398,9 +406,10 @@
|
|
398
406
|
"min": 0,
|
399
407
|
"max": 2000
|
400
408
|
},
|
409
|
+
"bg_color": "",
|
401
410
|
"steps": 16,
|
402
411
|
"fake_vid": false,
|
403
|
-
"scale_filter": "
|
412
|
+
"scale_filter": "bicubic",
|
404
413
|
"quantize_method": "imagequant",
|
405
414
|
"default_emoji": "😀"
|
406
415
|
},
|
@@ -443,9 +452,10 @@
|
|
443
452
|
"min": 0,
|
444
453
|
"max": 2000
|
445
454
|
},
|
455
|
+
"bg_color": "",
|
446
456
|
"steps": 16,
|
447
457
|
"fake_vid": false,
|
448
|
-
"scale_filter": "
|
458
|
+
"scale_filter": "bicubic",
|
449
459
|
"quantize_method": "imagequant",
|
450
460
|
"default_emoji": "😀"
|
451
461
|
},
|
@@ -488,9 +498,10 @@
|
|
488
498
|
"min": 0,
|
489
499
|
"max": 10000
|
490
500
|
},
|
501
|
+
"bg_color": "",
|
491
502
|
"steps": 16,
|
492
503
|
"fake_vid": false,
|
493
|
-
"scale_filter": "
|
504
|
+
"scale_filter": "bicubic",
|
494
505
|
"quantize_method": "imagequant",
|
495
506
|
"default_emoji": "😀"
|
496
507
|
}
|
@@ -38,6 +38,7 @@
|
|
38
38
|
"duration": "Change playback speed if outside of duration limit.\nDuration set in miliseconds.\n0 will disable limit.",
|
39
39
|
"duration_min": "Set minimum output duration in miliseconds.",
|
40
40
|
"duration_max": "Set maximum output duration in miliseconds.",
|
41
|
+
"bg_color": "Set custom background color.\nExample: 00ff00 for green.\nIf this is not set, background color would be auto set to black if image is bright, or white if image is dark.\nNote: The color should not be visible if output format supports transparency.",
|
41
42
|
"size": "Set maximum file size in bytes for video and image.",
|
42
43
|
"vid_size_max": "Set maximum file size limit for animated stickers.",
|
43
44
|
"img_size_max": "Set maximum file size limit for static stickers.",
|
@@ -45,7 +46,7 @@
|
|
45
46
|
"vid_format": "Set file format if input is animated.",
|
46
47
|
"img_format": "Set file format if input is static.",
|
47
48
|
"fake_vid": "Convert (faking) image to video.\nUseful if:\n(1) Size limit for video is larger than image;\n(2) Mix image and video into same pack.",
|
48
|
-
"scale_filter": "Set scale filter. Default as
|
49
|
+
"scale_filter": "Set scale filter. Default as bicubic. Valid options are:\n- nearest = Use nearest neighbour (Suitable for pixel art)\n-box = Similar to nearest, but better downscaling\n- bilinear = Linear interpolation\n- hamming = Similar to bilinear, but better downscaling\n- bicubic = Cubic spline interpolation\n- lanczos = A high-quality downsampling filter",
|
49
50
|
"quantize_method": "Set method for quantizing image. Default as imagequant. Valid options are:\n- imagequant = Best quality but slow\n- fastoctree = Fast but image looks chunky\n- none = No image quantizing, large image size as result",
|
50
51
|
"cache_dir": "Set custom cache directory.\nUseful for debugging, or speed up conversion if cache_dir is on RAM disk.",
|
51
52
|
"default_emoji": "Set the default emoji for uploading Signal and Telegram sticker packs."
|
@@ -230,8 +230,8 @@ class CodecInfo:
|
|
230
230
|
frames_to_iterate: Optional[int] = None,
|
231
231
|
frames_only: bool = False,
|
232
232
|
) -> Tuple[int, int]:
|
233
|
-
import av
|
234
|
-
from av.container.input import InputContainer
|
233
|
+
import av
|
234
|
+
from av.container.input import InputContainer
|
235
235
|
|
236
236
|
# Getting fps and frame count from metadata is not reliable
|
237
237
|
# Example: https://github.com/laggykiller/sticker-convert/issues/114
|
@@ -242,7 +242,7 @@ class CodecInfo:
|
|
242
242
|
else:
|
243
243
|
file_ref = BytesIO(file)
|
244
244
|
|
245
|
-
with av.open(file_ref) as container:
|
245
|
+
with av.open(file_ref) as container:
|
246
246
|
container = cast(InputContainer, container)
|
247
247
|
stream = container.streams.video[0]
|
248
248
|
if container.duration:
|
@@ -257,7 +257,7 @@ class CodecInfo:
|
|
257
257
|
|
258
258
|
frame_count = 0
|
259
259
|
last_frame = None
|
260
|
-
for frame_count, frame in enumerate(container.decode(stream)):
|
260
|
+
for frame_count, frame in enumerate(container.decode(stream)):
|
261
261
|
if frames_to_iterate is not None and frame_count == frames_to_iterate:
|
262
262
|
break
|
263
263
|
last_frame = frame
|
@@ -310,11 +310,11 @@ class CodecInfo:
|
|
310
310
|
if codec is not None:
|
311
311
|
return codec.lower()
|
312
312
|
|
313
|
-
import av
|
314
|
-
from av.error import InvalidDataError
|
313
|
+
import av
|
314
|
+
from av.error import InvalidDataError
|
315
315
|
|
316
316
|
try:
|
317
|
-
with av.open(file_ref) as container:
|
317
|
+
with av.open(file_ref) as container:
|
318
318
|
return container.streams.video[0].codec_context.name.lower()
|
319
319
|
except InvalidDataError:
|
320
320
|
pass
|
@@ -354,7 +354,7 @@ class CodecInfo:
|
|
354
354
|
else:
|
355
355
|
file_ref = BytesIO(file)
|
356
356
|
|
357
|
-
with av.open(file_ref) as container:
|
357
|
+
with av.open(file_ref) as container:
|
358
358
|
stream = container.streams.video[0]
|
359
359
|
width = stream.width
|
360
360
|
height = stream.height
|
sticker_convert/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sticker-convert
|
3
|
-
Version: 2.7.
|
3
|
+
Version: 2.7.9
|
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>
|
@@ -367,13 +367,14 @@ License-File: LICENSE
|
|
367
367
|
Requires-Dist: aiolimiter ~=1.1.0
|
368
368
|
Requires-Dist: anyio ~=3.7.1
|
369
369
|
Requires-Dist: apngasm-python ~=1.2.3
|
370
|
-
Requires-Dist: av ~=
|
370
|
+
Requires-Dist: av ~=12.0.0
|
371
371
|
Requires-Dist: beautifulsoup4 ~=4.12.3
|
372
372
|
Requires-Dist: rookiepy ~=0.3.6
|
373
373
|
Requires-Dist: imagequant ~=1.1.1
|
374
374
|
Requires-Dist: memory-tempfile ~=2.2.3
|
375
|
+
Requires-Dist: mergedeep ~=1.3.4
|
375
376
|
Requires-Dist: numpy >=1.22.4
|
376
|
-
Requires-Dist: Pillow
|
377
|
+
Requires-Dist: Pillow ==10.1.0
|
377
378
|
Requires-Dist: pyoxipng ~=9.0.0
|
378
379
|
Requires-Dist: python-telegram-bot ~=20.5
|
379
380
|
Requires-Dist: requests ~=2.31.0
|
@@ -482,18 +483,19 @@ usage: sticker-convert.py [-h] [--version] [--no-confirm] [--input-dir INPUT_DIR
|
|
482
483
|
[--preset {auto,signal,telegram,telegram_emoji,whatsapp,line,kakao,imessage_small,imessage_medium,imessage_large,custom}]
|
483
484
|
[--steps STEPS] [--processes PROCESSES] [--fps-min FPS_MIN] [--fps-max FPS_MAX]
|
484
485
|
[--fps-power FPS_POWER] [--res-min RES_MIN] [--res-max RES_MAX] [--res-w-min RES_W_MIN]
|
485
|
-
[--res-w-max RES_W_MAX] [--res-h-min RES_H_MIN] [--res-h-max RES_H_MAX] [--res-power RES_POWER]
|
486
|
+
[--res-w-max RES_W_MAX] [--res-h-min RES_H_MIN] [--res-h-max RES_H_MAX] [--res-power RES_POWER]
|
486
487
|
[--quality-min QUALITY_MIN] [--quality-max QUALITY_MAX] [--quality-power QUALITY_POWER]
|
487
488
|
[--color-min COLOR_MIN] [--color-max COLOR_MAX] [--color-power COLOR_POWER]
|
488
|
-
[--duration-min DURATION_MIN] [--duration-max DURATION_MAX] [--
|
489
|
-
[--
|
490
|
-
[--
|
491
|
-
[--
|
492
|
-
[--signal-
|
489
|
+
[--duration-min DURATION_MIN] [--duration-max DURATION_MAX] [--bg-color BG_COLOR]
|
490
|
+
[--vid-size-max VID_SIZE_MAX] [--img-size-max IMG_SIZE_MAX] [--vid-format VID_FORMAT]
|
491
|
+
[--img-format IMG_FORMAT] [--fake-vid] [--scale-filter SCALE_FILTER]
|
492
|
+
[--quantize-method QUANTIZE_METHOD] [--cache-dir CACHE_DIR] [--default-emoji DEFAULT_EMOJI]
|
493
|
+
[--signal-uuid SIGNAL_UUID] [--signal-password SIGNAL_PASSWORD] [--signal-get-auth]
|
494
|
+
[--signal-data-dir SIGNAL_DATA_DIR] [--telegram-token TELEGRAM_TOKEN]
|
493
495
|
[--telegram-userid TELEGRAM_USERID] [--kakao-auth-token KAKAO_AUTH_TOKEN] [--kakao-get-auth]
|
494
496
|
[--kakao-username KAKAO_USERNAME] [--kakao-password KAKAO_PASSWORD]
|
495
|
-
[--kakao-country-code KAKAO_COUNTRY_CODE] [--kakao-phone-number KAKAO_PHONE_NUMBER]
|
496
|
-
[--line-
|
497
|
+
[--kakao-country-code KAKAO_COUNTRY_CODE] [--kakao-phone-number KAKAO_PHONE_NUMBER] [--line-get-auth]
|
498
|
+
[--line-cookies LINE_COOKIES] [--save-cred SAVE_CRED]
|
497
499
|
|
498
500
|
CLI for stickers-convert
|
499
501
|
|
@@ -538,7 +540,7 @@ Output options:
|
|
538
540
|
|
539
541
|
Compression options:
|
540
542
|
--no-compress Do not compress files. Useful for only downloading stickers.
|
541
|
-
--preset {auto,signal,telegram,telegram_emoji,whatsapp,line,kakao,imessage_small,imessage_medium,imessage_large,custom}
|
543
|
+
--preset {auto,signal,telegram,telegram_emoji,whatsapp,line,kakao,imessage_small,imessage_medium,imessage_large,custom}
|
542
544
|
Apply preset for compression.
|
543
545
|
--steps STEPS Set number of divisions between min and max settings.
|
544
546
|
Steps higher = Slower but yields file more closer to the specified file size limit.
|
@@ -579,6 +581,10 @@ Compression options:
|
|
579
581
|
Set minimum output duration in miliseconds.
|
580
582
|
--duration-max DURATION_MAX
|
581
583
|
Set maximum output duration in miliseconds.
|
584
|
+
--bg-color BG_COLOR Set custom background color.
|
585
|
+
Example: 00ff00 for green.
|
586
|
+
If this is not set, background color would be auto set to black if image is bright, or white if image is dark.
|
587
|
+
Note: The color should not be visible if output format supports transparency.
|
582
588
|
--vid-size-max VID_SIZE_MAX
|
583
589
|
Set maximum file size limit for animated stickers.
|
584
590
|
--img-size-max IMG_SIZE_MAX
|
@@ -592,7 +598,7 @@ Compression options:
|
|
592
598
|
(1) Size limit for video is larger than image;
|
593
599
|
(2) Mix image and video into same pack.
|
594
600
|
--scale-filter SCALE_FILTER
|
595
|
-
Set scale filter. Default as
|
601
|
+
Set scale filter. Default as bicubic. Valid options are:
|
596
602
|
- nearest = Use nearest neighbour (Suitable for pixel art)
|
597
603
|
-box = Similar to nearest, but better downscaling
|
598
604
|
- bilinear = Linear interpolation
|
@@ -622,10 +628,10 @@ Credentials options:
|
|
622
628
|
--telegram-token TELEGRAM_TOKEN
|
623
629
|
Set Telegram token. Required for uploading and downloading Telegram stickers.
|
624
630
|
--telegram-userid TELEGRAM_USERID
|
625
|
-
Set telegram user_id (From real account, not bot account). Required for uploading Telegram stickers.
|
631
|
+
Set telegram user_id (From real account, not bot account). Required for uploading Telegram stickers.
|
626
632
|
--kakao-auth-token KAKAO_AUTH_TOKEN
|
627
|
-
Set Kakao auth_token. Required for downloading animated stickers from https://e.kakao.com/t/xxxxx
|
628
|
-
--kakao-get-auth Generate Kakao auth_token. Kakao username, password, country code and phone number are also required.
|
633
|
+
Set Kakao auth_token. Required for downloading animated stickers from https://e.kakao.com/t/xxxxx
|
634
|
+
--kakao-get-auth Generate Kakao auth_token. Kakao username, password, country code and phone number are also required.
|
629
635
|
--kakao-username KAKAO_USERNAME
|
630
636
|
Set Kakao username, which is email or phone number used for signing up Kakao account
|
631
637
|
Example: +447700900142
|
@@ -1,12 +1,12 @@
|
|
1
1
|
sticker_convert/__init__.py,sha256=iQnv6UOOA69c3soAn7ZOnAIubTIQSUxtq1Uhh8xRWvU,102
|
2
2
|
sticker_convert/__main__.py,sha256=6RJauR-SCSSTT3TU7FFB6B6PVwsCxO2xZXtmZ3jc2Is,463
|
3
|
-
sticker_convert/cli.py,sha256=
|
4
|
-
sticker_convert/converter.py,sha256=
|
3
|
+
sticker_convert/cli.py,sha256=Ex5Nculo1cbkfhLPibnYR6-SM431ODGPb324Bos0z6k,17702
|
4
|
+
sticker_convert/converter.py,sha256=XYt6bt7hnCJSWK-oWvVUQGCHP2Z-g2xVQWyu4j7oQek,32955
|
5
5
|
sticker_convert/definitions.py,sha256=ZhP2ALCEud-w9ZZD4c3TDG9eHGPZyaAL7zPUsJAbjtE,2073
|
6
|
-
sticker_convert/gui.py,sha256=
|
7
|
-
sticker_convert/job.py,sha256=
|
8
|
-
sticker_convert/job_option.py,sha256=
|
9
|
-
sticker_convert/version.py,sha256=
|
6
|
+
sticker_convert/gui.py,sha256=TqdgFbHBRYgcXWWrsfxLUJ8Zu9WeE11vYyZMX6nalik,30599
|
7
|
+
sticker_convert/job.py,sha256=hJgjMAvv50PZ4eYB_ZV7uXc8ZqF7sKnbRvCzs1YzgGE,26144
|
8
|
+
sticker_convert/job_option.py,sha256=1YVhyTfu2cWz3qpAKbdIM11jbL0CJz0ksOYAeg8v6dc,7649
|
9
|
+
sticker_convert/version.py,sha256=YtYyLos6wcoE1gtu4LiuT2txOQo9u2LqLrvBoQ9NGb4,46
|
10
10
|
sticker_convert/downloaders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
11
|
sticker_convert/downloaders/download_base.py,sha256=5R7c8kwahAflOOYrtSQPnBVQ4T-DsprPUMP7G9wcJX4,2824
|
12
12
|
sticker_convert/downloaders/download_kakao.py,sha256=RYrebTxEjKjXAr9xw18r0dMW0dFjSqZxzi8dpaq56TY,8581
|
@@ -16,7 +16,7 @@ sticker_convert/downloaders/download_telegram.py,sha256=OZdg7WuJV5VurZVC6dZSnzCW
|
|
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
|
18
18
|
sticker_convert/gui_components/frames/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
|
-
sticker_convert/gui_components/frames/comp_frame.py,sha256=
|
19
|
+
sticker_convert/gui_components/frames/comp_frame.py,sha256=9k_UntKKi2G_g0byzoj1rdTqOq7q9mcnXiy799bYnr0,7257
|
20
20
|
sticker_convert/gui_components/frames/config_frame.py,sha256=b3X4QAnGpde0OhthXHmjSyU_Yb5tYRUFXmy04Yi8Zmo,4316
|
21
21
|
sticker_convert/gui_components/frames/control_frame.py,sha256=_XiOJ9JPUfiysDghGG04sEVLrXG9TMVlDZ60W0LhYVI,835
|
22
22
|
sticker_convert/gui_components/frames/cred_frame.py,sha256=I3XrOv7kUOsvFWquuzWWpZWbLclqKQXgD7dx5pcTuY4,6499
|
@@ -25,7 +25,7 @@ sticker_convert/gui_components/frames/output_frame.py,sha256=n2WLk22h61DoZli8WbF
|
|
25
25
|
sticker_convert/gui_components/frames/progress_frame.py,sha256=LWUZg_iL7iiNTfu7N5Ct_pklZdghxihENi7DP9YozOE,4915
|
26
26
|
sticker_convert/gui_components/frames/right_clicker.py,sha256=dGIvSzEChrkguR80pzUemBNJ39uzJjVJQKeDNUoW3Gk,721
|
27
27
|
sticker_convert/gui_components/windows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
28
|
-
sticker_convert/gui_components/windows/advanced_compression_window.py,sha256=
|
28
|
+
sticker_convert/gui_components/windows/advanced_compression_window.py,sha256=I3I3seZ5xLTEIsJt3A4t4vsF9ZFY3DmxJVo_yD4HSfA,30639
|
29
29
|
sticker_convert/gui_components/windows/base_window.py,sha256=xBE1peGMPvWsdrFej0CJUVhmQ57GJGvz-cX03nIIhkE,1108
|
30
30
|
sticker_convert/gui_components/windows/kakao_get_auth_window.py,sha256=AvXB2Q8pAPkKILHTvlLGV9jfBcvskCA4arko4nvBTdo,7115
|
31
31
|
sticker_convert/gui_components/windows/line_get_auth_window.py,sha256=S4ES_lk2-GDvPokZtYALnUc5zW1VbS4WulNqO9K1aSs,3375
|
@@ -66,9 +66,9 @@ sticker_convert/resources/NotoColorEmoji.ttf,sha256=LurIVaCIA8bSCfjrdO1feYr0bhKL
|
|
66
66
|
sticker_convert/resources/appicon.icns,sha256=FB2DVTOQcFfQNZ9RcyG3z9c9k7eOiI1qw0IJhXMRFg4,5404
|
67
67
|
sticker_convert/resources/appicon.ico,sha256=-ldugcl2Yq2pBRTktnhGKWInpKyWzRjCiPvMr3XPTlc,38078
|
68
68
|
sticker_convert/resources/appicon.png,sha256=6XBEQz7PnerqS43aRkwpWolFG4WvKMuQ-st1ly-_JPg,5265
|
69
|
-
sticker_convert/resources/compression.json,sha256=
|
69
|
+
sticker_convert/resources/compression.json,sha256=vwn6wLsfsPnNnpjhJHRvzozBI4F_kkb9-eBR8IlyYLo,10833
|
70
70
|
sticker_convert/resources/emoji.json,sha256=sXSuKusyG1L2Stuf9BL5ZqfzOIOdeAeE3RXcrXAsLdY,413367
|
71
|
-
sticker_convert/resources/help.json,sha256=
|
71
|
+
sticker_convert/resources/help.json,sha256=dYLh751rhhriSwc2p_xKVdWPCgUZTPyNJ1QFZdUjA2M,6259
|
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
|
@@ -89,12 +89,12 @@ sticker_convert/utils/files/metadata_handler.py,sha256=TJpQ-7KdnqQh09hwR6xB_scRL
|
|
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
|
-
sticker_convert/utils/media/codec_info.py,sha256=
|
92
|
+
sticker_convert/utils/media/codec_info.py,sha256=JHHlfCLSpepYFv6TSL1XqwUJoVOpOPFxurRtduplPGY,12856
|
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.7.
|
96
|
-
sticker_convert-2.7.
|
97
|
-
sticker_convert-2.7.
|
98
|
-
sticker_convert-2.7.
|
99
|
-
sticker_convert-2.7.
|
100
|
-
sticker_convert-2.7.
|
95
|
+
sticker_convert-2.7.9.dist-info/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
|
96
|
+
sticker_convert-2.7.9.dist-info/METADATA,sha256=zUUav4-oua996FFdpYa9TqeXRVfj2X7w1jhK-Xcpg4I,48836
|
97
|
+
sticker_convert-2.7.9.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
98
|
+
sticker_convert-2.7.9.dist-info/entry_points.txt,sha256=MNJ7XyC--ugxi5jS1nzjDLGnxCyLuaGdsVLnJhDHCqs,66
|
99
|
+
sticker_convert-2.7.9.dist-info/top_level.txt,sha256=r9vfnB0l1ZnH5pTH5RvkobnK3Ow9m0RsncaOMAtiAtk,16
|
100
|
+
sticker_convert-2.7.9.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|